<commit_before>//============================================================================
// vSMC/include/vsmc/core/monitor.hpp
//----------------------------------------------------------------------------
//                         vSMC: Scalable Monte Carlo
//----------------------------------------------------------------------------
// Copyright (c) 2013-2015, Yan Zhou
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
//   Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//============================================================================

#ifndef VSMC_CORE_MONITOR_HPP
#define VSMC_CORE_MONITOR_HPP

#include <vsmc/internal/common.hpp>
#include <vsmc/utility/aligned_memory.hpp>
#if VSMC_HAS_MKL
#include <vsmc/utility/mkl.hpp>
#endif

#define VSMC_RUNTIME_ASSERT_CORE_MONITOR_ID(func)                             \
    VSMC_RUNTIME_ASSERT(                                                      \
        (id < dim()), "**Monitor::" #func "** INVALID ID NUMBER ARGUMENT")

#define VSMC_RUNTIME_ASSERT_CORE_MONITOR_ITER(func)                           \
    VSMC_RUNTIME_ASSERT((iter < iter_size()),                                 \
        "**Monitor::" #func "** INVALID ITERATION NUMBER ARGUMENT")

#define VSMC_RUNTIME_ASSERT_CORE_MONITOR_FUNCTOR(func, caller, name)          \
    VSMC_RUNTIME_ASSERT(static_cast<bool>(func),                              \
        "**Monitor::" #caller "** INVALID " #name " OBJECT")

namespace vsmc
{

/// \brief Monitor for Monte Carlo integration
/// \ingroup Core
template <typename T>
class Monitor
{
    public:
    typedef T value_type;
    typedef std::function<void(
        std::size_t, std::size_t, const Particle<T> &, double *)> eval_type;

    /// \brief Construct a Monitor with an evaluation object
    ///
    /// \param dim The dimension of the Monitor, i.e., the number of variables
    /// \param eval The evaluation object of type Monitor::eval_type
    /// \param record_only The Monitor only records results instead of
    /// calculating them itself
    /// \param stage The stage of the Monitor. A Monitor may be evaluated
    /// after
    /// all steps that move the particles but before any resampling
    /// (`MonitorMove`), or the possible resampling step but before any MCMC
    /// steps (`MonitorResample`), all after all MCMC steps (`MonitorMCMC`).
    /// If
    /// a Monitor is present during initialization, then the initialization
    /// are
    /// taken as the step that moves particles, and both `MonitorResample` and
    /// `MonitorMCMC` are considered after the possible resampling.
    ///
    /// The evaluation object has the signature
    /// ~~~{.cpp}
    /// void eval (std::size_t iter, std::size_t dim, const Particle<T>
    /// &particle, double *result)
    /// ~~~
    /// where the first three arguments are passed in by the Sampler at the
    /// end of each iteration. The evaluation occurs after the possible MCMC
    /// moves. The output parameter `result` shall contain the results of the
    /// evaluation.
    ///
    /// If `record_only` is true, then the monitor only records the values
    /// stored in `result`. Otherwise, the behavior is explained below
    ///
    /// The array `result` is of length `particle.size() * dim`, and it
    /// represents a row major matrix of dimension `particle.size()` by `dim`,
    /// say \f$R\f$. Let \f$W\f$ be the vector of the normalized weights. The
    /// Monitor will be respoinsible to compute the importance sampling
    /// estimate \f$r = R^TW\f$ and record it. For example, say the purpose of
    /// the Monitor is to record the importance sampling estimates of
    /// \f$E[h(X)]\f$ where \f$h(X) = (h_1(X),\dots,h_d(X))\f$. Then `result`
    /// shall contain the evaluation of \f$h(X_i)\f$ for each \f$i\f$ from `0`
    /// to `particle.size() - 1` in the order
    /// \f$(h_1(X_0), \dots, h_d(X_0), h_1(X_1), \dots, h_d(X_1), \dots)\f$.
    ///
    /// After each evaluation, the iteration number `iter` and the imporatance
    /// sampling estimates are recorded and can be retrived by `index()` and
    /// `record()`.
    explicit Monitor(std::size_t dim, const eval_type &eval,
        bool record_only = false, MonitorStage stage = MonitorMCMC)
        : dim_(dim)
        , eval_(eval)
        , recording_(true)
        , record_only_(record_only)
        , stage_(stage)
        , name_(dim)
    {
    }

    /// \brief The dimension of the Monitor
    std::size_t dim() const { return dim_; }

    /// \brief If this is a record only Monitor
    bool record_only() const { return record_only_; }

    /// \brief The stage of the Montior
    MonitorStage stage() const { return stage_; }

    /// \brief The number of iterations has been recorded
    ///
    /// \details
    /// This is not necessarily the same as Sampler<T>::iter_size. For
    /// example, a Monitor can be added only after a certain time point of the
    /// sampler's iterations. Also the Monitor can be turned off for a period
    /// during the iterations.
    std::size_t iter_size() const { return index_.size(); }

    /// \brief Reserve space for a specified number of iterations
    void reserve(std::size_t num)
    {
        index_.reserve(num);
        record_.reserve(dim_ * num);
    }

    /// \brief Whether the evaluation object is valid
    bool empty() const { return !static_cast<bool>(eval_); }

    /// \brief Read and write access to the names of variables
    ///
    /// \details
    /// By default, each variable of a Monitor is unnamed and the returned
    /// string is empty. However, the user can selectively set the names of
    /// each variable. This effect how Sampler will print the headers of the
    /// summary table.
    std::string &name(std::size_t id)
    {
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ID(name);

        return name_[id];
    }

    /// \brief Read only access to the names of variables
    const std::string &name(std::size_t id) const
    {
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ID(name);

        return name_[id];
    }

    /// \brief Get the iteration index of the sampler of a given Monitor
    /// iteration
    ///
    /// \details
    /// For example, if a Monitor is only added to the sampler at the
    /// sampler's iteration `siter`. Then `index(0)` will be `siter` and so
    /// on. If the Monitor is added before the sampler's initialization and
    /// continued to be evaluated during the iterations without calling
    /// `turnoff()`, then iter(iter) shall just be `iter`.
    std::size_t index(std::size_t iter) const
    {
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ITER(index);

        return index_[iter];
    }

    /// \brief Get the latest Monte Carlo integration record of a given
    /// variable
    ///
    /// \details
    /// For a `dim` dimension Monitor, `id` shall be 0 to `dim` - 1
    double record(std::size_t id) const
    {
        std::size_t iter = iter_size() ? iter_size() - 1 : iter_size();
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ID(record);
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ITER(record);

        return record_[iter * dim_ + id];
    }

    /// \brief Get the Monte Carlo integration record of a given variable and
    /// the Monitor iteration
    ///
    /// \details
    /// For a `dim` dimension Monitor, `id` shall be 0 to `dim` - 1
    double record(std::size_t id, std::size_t iter) const
    {
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ID(record);
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ITER(record);

        return record_[iter * dim_ + id];
    }

    /// \brief Read the index history through an output iterator
    template <typename OutputIter>
    void read_index(OutputIter first) const
    {
        std::copy(index_.begin(), index_.end(), first);
    }

    /// \brief Read only access to the raw data of the index vector
    const std::size_t *index_data() const { return index_.data(); }

    /// \brief Read only access to the raw data of records (a row major
    /// matrix)
    const double *record_data() const { return record_.data(); }

    /// \brief Read only access to the raw data of records for a given
    /// Monitor iteration
    const double *record_data(std::size_t iter) const
    {
        return record_.data() + iter * dim_;
    }

    /// \brief Read the record history for a given variable through an output
    /// iterator
    template <typename OutputIter>
    void read_record(std::size_t id, OutputIter first) const
    {
        const std::size_t N = iter_size();
        const double *riter = record_.data() + id;
        for (std::size_t i = 0; i != N; ++i, ++first, riter += dim_)
            *first = *riter;
    }

    /// \brief Read the record history of all variables through an array of
    /// output iterators
    ///
    /// \param first An iterator of container of output iterators
    template <typename OutputIterIter>
    void read_record_matrix(OutputIterIter first) const
    {
        for (std::size_t d = 0; d != dim_; ++d, ++first)
            read_record(d, *first);
    }

    /// \brief Read the record history of all variables through an output
    /// iterator
    ///
    /// \param first The output iterator
    ///
    /// For example, say `first` is of type `double *`, then if `order ==
    /// ColMajor`, then, `first[j * iter_size() + i] == record(i, j)`.
    /// Otherwise, if `order == RowMajor`, then `first[i * dim() + j] ==
    /// record(i, j)`. That is, the output is an `iter_size()` by `dim()`
    /// matrix, with the usual meaning of column or row major order.
    template <MatrixOrder Order, typename OutputIter>
    void read_record_matrix(OutputIter first) const
    {
        const std::size_t N = iter_size();
        if (Order == ColMajor) {
            for (std::size_t d = 0; d != dim_; ++d) {
                const double *riter = record_.data() + d;
                for (std::size_t i = 0; i != N; ++i, ++first, riter += dim_)
                    *first = *riter;
            }
        }

        if (Order == RowMajor)
            std::copy(record_.begin(), record_.end(), first);
    }

    /// \brief Set a new evaluation object of type eval_type
    void set_eval(const eval_type &new_eval) { eval_ = new_eval; }

    /// \brief Perform the evaluation for a given iteration and a Particle<T>
    /// object.
    ///
    /// \details
    /// This function is called by a Sampler at the end of each
    /// iteration. It does nothing if `recording()` returns `false`. Otherwise
    /// it use the user defined evaluation object to compute results. When a
    /// Monitor is constructed, `recording()` always returns `true`. It can be
    /// turned off by `turn_off()` and turned on later by `turn_on()`.
    void eval(
        std::size_t iter, const Particle<T> &particle, MonitorStage stage)
    {
        if (!recording_)
            return;

        if (stage != stage_)
            return;

        VSMC_RUNTIME_ASSERT_CORE_MONITOR_FUNCTOR(eval_, eval, EVALUATION);

        result_.resize(dim_);
        if (record_only_) {
            eval_(iter, dim_, particle, result_.data());
            push_back(iter);

            return;
        }

        const std::size_t N = static_cast<std::size_t>(particle.size());
        buffer_.resize(N * dim_);
        eval_(iter, dim_, particle, buffer_.data());
#if VSMC_HAS_MKL
        if (ss_task_.ptr() == nullptr) {
            MKL_INT p = static_cast<MKL_INT>(dim_);
            MKL_INT n = static_cast<MKL_INT>(N);
            MKL_INT xstorage = VSL_SS_MATRIX_STORAGE_COLS;
            ss_task_.reset(
                &p, &n, &xstorage, buffer_.data(), result_.data(), nullptr);
        }
        ::vsldSSCompute(ss_task_.ptr(), VSL_SS_MEAN, VSL_SS_METHOD_FAST);
#ifdef VSMC_CBLAS_INT
        ::cblas_dgemv(::CblasColMajor, ::CblasNoTrans,
            static_cast<VSMC_CBLAS_INT>(dim_), static_cast<VSMC_CBLAS_INT>(N),
            1, buffer_.data(), static_cast<VSMC_CBLAS_INT>(dim_),
            particle.weight_set().weight_data(), 1, 0, result_.data(), 1);
#else  // VSMC_CBLAS_INT
        const double *wptr = particle.weight_set().weight_data();
        const double *bptr = buffer_.data();
        std::fill(result_.begin(), result_.end(), 0.0);
        for (std::size_t i = 0; i != N; ++i)
            for (std::size_t d = 0; d != dim_; ++d, ++bptr)
                result_[d] += particle.weight_set().weight(i) * (*bptr);
#endif // VSMC_CBLAS_INT
#endif // VSMC_HAS_MKL
        push_back(iter);
    }

    /// \brief Clear all records of the index and integrations
    void clear()
    {
        index_.clear();
        record_.clear();
    }

    /// \brief Whether the Monitor is actively recording results
    bool recording() const { return recording_; }

    /// \brief Turn on the recording
    void turn_on() { recording_ = true; }

    /// \brief Turn off the recording
    void turn_off() { recording_ = false; }

    private:
    std::size_t dim_;
    eval_type eval_;
    bool recording_;
    bool record_only_;
    MonitorStage stage_;
    std::vector<std::string> name_;
    std::vector<std::size_t> index_;
    AlignedVector<double> record_;
    AlignedVector<double> result_;
    AlignedVector<double> buffer_;
#if VSMC_HAS_MKL
    MKLSSTask<double> ss_task_;
#endif

    void push_back(std::size_t iter)
    {
        index_.push_back(iter);
        record_.insert(record_.end(), result_.begin(), result_.end());
    }
}; // class Monitor

} // namespace vsmc

#endif // VSMC_CORE_MONITOR_HPP
<commit_msg>Fix Monitor eval<commit_after>//============================================================================
// vSMC/include/vsmc/core/monitor.hpp
//----------------------------------------------------------------------------
//                         vSMC: Scalable Monte Carlo
//----------------------------------------------------------------------------
// Copyright (c) 2013-2015, Yan Zhou
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
//   Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//============================================================================

#ifndef VSMC_CORE_MONITOR_HPP
#define VSMC_CORE_MONITOR_HPP

#include <vsmc/internal/common.hpp>
#include <vsmc/utility/aligned_memory.hpp>
#if VSMC_HAS_MKL
#include <vsmc/utility/mkl.hpp>
#endif

#define VSMC_RUNTIME_ASSERT_CORE_MONITOR_ID(func)                             \
    VSMC_RUNTIME_ASSERT(                                                      \
        (id < dim()), "**Monitor::" #func "** INVALID ID NUMBER ARGUMENT")

#define VSMC_RUNTIME_ASSERT_CORE_MONITOR_ITER(func)                           \
    VSMC_RUNTIME_ASSERT((iter < iter_size()),                                 \
        "**Monitor::" #func "** INVALID ITERATION NUMBER ARGUMENT")

#define VSMC_RUNTIME_ASSERT_CORE_MONITOR_FUNCTOR(func, caller, name)          \
    VSMC_RUNTIME_ASSERT(static_cast<bool>(func),                              \
        "**Monitor::" #caller "** INVALID " #name " OBJECT")

namespace vsmc
{

/// \brief Monitor for Monte Carlo integration
/// \ingroup Core
template <typename T>
class Monitor
{
    public:
    typedef T value_type;
    typedef std::function<void(
        std::size_t, std::size_t, const Particle<T> &, double *)> eval_type;

    /// \brief Construct a Monitor with an evaluation object
    ///
    /// \param dim The dimension of the Monitor, i.e., the number of variables
    /// \param eval The evaluation object of type Monitor::eval_type
    /// \param record_only The Monitor only records results instead of
    /// calculating them itself
    /// \param stage The stage of the Monitor. A Monitor may be evaluated
    /// after
    /// all steps that move the particles but before any resampling
    /// (`MonitorMove`), or the possible resampling step but before any MCMC
    /// steps (`MonitorResample`), all after all MCMC steps (`MonitorMCMC`).
    /// If
    /// a Monitor is present during initialization, then the initialization
    /// are
    /// taken as the step that moves particles, and both `MonitorResample` and
    /// `MonitorMCMC` are considered after the possible resampling.
    ///
    /// The evaluation object has the signature
    /// ~~~{.cpp}
    /// void eval (std::size_t iter, std::size_t dim, const Particle<T>
    /// &particle, double *result)
    /// ~~~
    /// where the first three arguments are passed in by the Sampler at the
    /// end of each iteration. The evaluation occurs after the possible MCMC
    /// moves. The output parameter `result` shall contain the results of the
    /// evaluation.
    ///
    /// If `record_only` is true, then the monitor only records the values
    /// stored in `result`. Otherwise, the behavior is explained below
    ///
    /// The array `result` is of length `particle.size() * dim`, and it
    /// represents a row major matrix of dimension `particle.size()` by `dim`,
    /// say \f$R\f$. Let \f$W\f$ be the vector of the normalized weights. The
    /// Monitor will be respoinsible to compute the importance sampling
    /// estimate \f$r = R^TW\f$ and record it. For example, say the purpose of
    /// the Monitor is to record the importance sampling estimates of
    /// \f$E[h(X)]\f$ where \f$h(X) = (h_1(X),\dots,h_d(X))\f$. Then `result`
    /// shall contain the evaluation of \f$h(X_i)\f$ for each \f$i\f$ from `0`
    /// to `particle.size() - 1` in the order
    /// \f$(h_1(X_0), \dots, h_d(X_0), h_1(X_1), \dots, h_d(X_1), \dots)\f$.
    ///
    /// After each evaluation, the iteration number `iter` and the imporatance
    /// sampling estimates are recorded and can be retrived by `index()` and
    /// `record()`.
    explicit Monitor(std::size_t dim, const eval_type &eval,
        bool record_only = false, MonitorStage stage = MonitorMCMC)
        : dim_(dim)
        , eval_(eval)
        , recording_(true)
        , record_only_(record_only)
        , stage_(stage)
        , name_(dim)
    {
    }

    /// \brief The dimension of the Monitor
    std::size_t dim() const { return dim_; }

    /// \brief If this is a record only Monitor
    bool record_only() const { return record_only_; }

    /// \brief The stage of the Montior
    MonitorStage stage() const { return stage_; }

    /// \brief The number of iterations has been recorded
    ///
    /// \details
    /// This is not necessarily the same as Sampler<T>::iter_size. For
    /// example, a Monitor can be added only after a certain time point of the
    /// sampler's iterations. Also the Monitor can be turned off for a period
    /// during the iterations.
    std::size_t iter_size() const { return index_.size(); }

    /// \brief Reserve space for a specified number of iterations
    void reserve(std::size_t num)
    {
        index_.reserve(num);
        record_.reserve(dim_ * num);
    }

    /// \brief Whether the evaluation object is valid
    bool empty() const { return !static_cast<bool>(eval_); }

    /// \brief Read and write access to the names of variables
    ///
    /// \details
    /// By default, each variable of a Monitor is unnamed and the returned
    /// string is empty. However, the user can selectively set the names of
    /// each variable. This effect how Sampler will print the headers of the
    /// summary table.
    std::string &name(std::size_t id)
    {
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ID(name);

        return name_[id];
    }

    /// \brief Read only access to the names of variables
    const std::string &name(std::size_t id) const
    {
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ID(name);

        return name_[id];
    }

    /// \brief Get the iteration index of the sampler of a given Monitor
    /// iteration
    ///
    /// \details
    /// For example, if a Monitor is only added to the sampler at the
    /// sampler's iteration `siter`. Then `index(0)` will be `siter` and so
    /// on. If the Monitor is added before the sampler's initialization and
    /// continued to be evaluated during the iterations without calling
    /// `turnoff()`, then iter(iter) shall just be `iter`.
    std::size_t index(std::size_t iter) const
    {
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ITER(index);

        return index_[iter];
    }

    /// \brief Get the latest Monte Carlo integration record of a given
    /// variable
    ///
    /// \details
    /// For a `dim` dimension Monitor, `id` shall be 0 to `dim` - 1
    double record(std::size_t id) const
    {
        std::size_t iter = iter_size() ? iter_size() - 1 : iter_size();
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ID(record);
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ITER(record);

        return record_[iter * dim_ + id];
    }

    /// \brief Get the Monte Carlo integration record of a given variable and
    /// the Monitor iteration
    ///
    /// \details
    /// For a `dim` dimension Monitor, `id` shall be 0 to `dim` - 1
    double record(std::size_t id, std::size_t iter) const
    {
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ID(record);
        VSMC_RUNTIME_ASSERT_CORE_MONITOR_ITER(record);

        return record_[iter * dim_ + id];
    }

    /// \brief Read the index history through an output iterator
    template <typename OutputIter>
    void read_index(OutputIter first) const
    {
        std::copy(index_.begin(), index_.end(), first);
    }

    /// \brief Read only access to the raw data of the index vector
    const std::size_t *index_data() const { return index_.data(); }

    /// \brief Read only access to the raw data of records (a row major
    /// matrix)
    const double *record_data() const { return record_.data(); }

    /// \brief Read only access to the raw data of records for a given
    /// Monitor iteration
    const double *record_data(std::size_t iter) const
    {
        return record_.data() + iter * dim_;
    }

    /// \brief Read the record history for a given variable through an output
    /// iterator
    template <typename OutputIter>
    void read_record(std::size_t id, OutputIter first) const
    {
        const std::size_t N = iter_size();
        const double *riter = record_.data() + id;
        for (std::size_t i = 0; i != N; ++i, ++first, riter += dim_)
            *first = *riter;
    }

    /// \brief Read the record history of all variables through an array of
    /// output iterators
    ///
    /// \param first An iterator of container of output iterators
    template <typename OutputIterIter>
    void read_record_matrix(OutputIterIter first) const
    {
        for (std::size_t d = 0; d != dim_; ++d, ++first)
            read_record(d, *first);
    }

    /// \brief Read the record history of all variables through an output
    /// iterator
    ///
    /// \param first The output iterator
    ///
    /// For example, say `first` is of type `double *`, then if `order ==
    /// ColMajor`, then, `first[j * iter_size() + i] == record(i, j)`.
    /// Otherwise, if `order == RowMajor`, then `first[i * dim() + j] ==
    /// record(i, j)`. That is, the output is an `iter_size()` by `dim()`
    /// matrix, with the usual meaning of column or row major order.
    template <MatrixOrder Order, typename OutputIter>
    void read_record_matrix(OutputIter first) const
    {
        const std::size_t N = iter_size();
        if (Order == ColMajor) {
            for (std::size_t d = 0; d != dim_; ++d) {
                const double *riter = record_.data() + d;
                for (std::size_t i = 0; i != N; ++i, ++first, riter += dim_)
                    *first = *riter;
            }
        }

        if (Order == RowMajor)
            std::copy(record_.begin(), record_.end(), first);
    }

    /// \brief Set a new evaluation object of type eval_type
    void set_eval(const eval_type &new_eval) { eval_ = new_eval; }

    /// \brief Perform the evaluation for a given iteration and a Particle<T>
    /// object.
    ///
    /// \details
    /// This function is called by a Sampler at the end of each
    /// iteration. It does nothing if `recording()` returns `false`. Otherwise
    /// it use the user defined evaluation object to compute results. When a
    /// Monitor is constructed, `recording()` always returns `true`. It can be
    /// turned off by `turn_off()` and turned on later by `turn_on()`.
    void eval(
        std::size_t iter, const Particle<T> &particle, MonitorStage stage)
    {
        if (!recording_)
            return;

        if (stage != stage_)
            return;

        VSMC_RUNTIME_ASSERT_CORE_MONITOR_FUNCTOR(eval_, eval, EVALUATION);

        result_.resize(dim_);
        if (record_only_) {
            eval_(iter, dim_, particle, result_.data());
            push_back(iter);

            return;
        }

        const std::size_t N = static_cast<std::size_t>(particle.size());
        buffer_.resize(N * dim_);
        eval_(iter, dim_, particle, buffer_.data());

#if VSMC_HAS_MKL
        MKL_INT p = static_cast<MKL_INT>(dim_);
        MKL_INT n = static_cast<MKL_INT>(N);
        MKL_INT xstorage = VSL_SS_MATRIX_STORAGE_COLS;
        const double *const x = buffer_.data();
        const double *const w = particle.weight_set().weight_data();
        const double *const s = result_.data();
        if (ss_task_.get() == nullptr)
            ss_task_.reset(&p, &n, &xstorage, x, w);
        ::vsliSSEditTask(ss_task_.ptr(), VSL_SS_ED_DIMEN, &p);
        ::vsliSSEditTask(ss_task_.ptr(), VSL_SS_ED_OBSERV_N, &n);
        ::vsliSSEditTask(ss_task_.ptr(), VSL_SS_ED_OBSERV_STORAGE, &xstorage);
        ::vsldSSEditTask(ss_task_.ptr(), VSL_SS_ED_OBSERV, x);
        ::vsldSSEditTask(ss_task_.ptr(), VSL_SS_ED_WEIGHTS, w);
        ::vsldSSEditTask(ss_task_.ptr(), VSL_SS_ED_SUM, s);
        ::vsldSSCompute(ss_task_.ptr(), VSL_SS_SUM, VSL_SS_METHOD_FAST);
#else // VSMC_HAS_MKL
#ifdef VSMC_CBLAS_INT
        ::cblas_dgemv(::CblasColMajor, ::CblasNoTrans,
            static_cast<VSMC_CBLAS_INT>(dim_), static_cast<VSMC_CBLAS_INT>(N),
            1, buffer_.data(), static_cast<VSMC_CBLAS_INT>(dim_),
            particle.weight_set().weight_data(), 1, 0, result_.data(), 1);
#else  // VSMC_CBLAS_INT
        const double *wptr = particle.weight_set().weight_data();
        const double *bptr = buffer_.data();
        std::fill(result_.begin(), result_.end(), 0.0);
        for (std::size_t i = 0; i != N; ++i)
            for (std::size_t d = 0; d != dim_; ++d, ++bptr)
                result_[d] += particle.weight_set().weight(i) * (*bptr);
#endif // VSMC_CBLAS_INT
#endif // VSMC_HAS_MKL

        push_back(iter);
    }

    /// \brief Clear all records of the index and integrations
    void clear()
    {
        index_.clear();
        record_.clear();
    }

    /// \brief Whether the Monitor is actively recording results
    bool recording() const { return recording_; }

    /// \brief Turn on the recording
    void turn_on() { recording_ = true; }

    /// \brief Turn off the recording
    void turn_off() { recording_ = false; }

    private:
    std::size_t dim_;
    eval_type eval_;
    bool recording_;
    bool record_only_;
    MonitorStage stage_;
    std::vector<std::string> name_;
    std::vector<std::size_t> index_;
    AlignedVector<double> record_;
    AlignedVector<double> result_;
    AlignedVector<double> buffer_;
#if VSMC_HAS_MKL
    MKLSSTask<double> ss_task_;
#endif

    void push_back(std::size_t iter)
    {
        index_.push_back(iter);
        record_.insert(record_.end(), result_.begin(), result_.end());
    }
}; // class Monitor

} // namespace vsmc

#endif // VSMC_CORE_MONITOR_HPP
<|endoftext|>


<commit_before>//=============================================================================================================
/**
 * @file     main.cpp
 * @author   Ruben Dörfel <ruben.doerfel@tu-ilmenau.de>;
 * @since    0.1.0
 * @date     February, 2020
 *
 * @section  LICENSE
 *
 * Copyright (C) 2020, Ruben Dörfel. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 * the following conditions are met:
 *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *       following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 *       the following disclaimer in the documentation and/or other materials provided with the distribution.
 *     * Neither the name of MNE-CPP authors nor the names of its contributors may be used
 *       to endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * @brief     Test for fitHpi function in inverse library.
 *
 */

//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include <utils/generics/applicationlogger.h>

#include <iostream>
#include <vector>
#include <math.h>

#include <fiff/fiff.h>
#include <fiff/fiff_info.h>
#include <fiff/fiff_dig_point_set.h>

#include <inverse/hpiFit/hpifit.h>
#include <inverse/hpiFit/hpifitdata.h>

#include <utils/ioutils.h>
#include <utils/mnemath.h>

#include <fwd/fwd_coil_set.h>

#include <Eigen/Dense>

//=============================================================================================================
// QT INCLUDES
//=============================================================================================================

#include <QtCore/QCoreApplication>
#include <QFile>
#include <QCommandLineParser>
#include <QDebug>
#include <QtTest>

//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================

using namespace FIFFLIB;
using namespace UTILSLIB;
using namespace INVERSELIB;
using namespace Eigen;

//=============================================================================================================
/**
 * DECLARE CLASS TestHpiFit
 *
 * @brief The TestHpiFit class provides hpi fit verifivcation tests
 *
 */
class TestHpiFit: public QObject
{
    Q_OBJECT

public:
    TestHpiFit();

private slots:
    void initTestCase();
    void testDataPrepatationFinished();
    void compareFrequencies();
    void compareTranslation();
    void compareRotation();
    void compareAngle();
    void compareMove();
    void compareDetect();
    void compareTime();
    void cleanupTestCase();

private:
    double dErrorTrans = 0.0003;
    double dErrorQuat = 0.002;
    double dErrorTime = 0.00000001;
    double dErrorAngle = 0.1;
    double dErrorDetect = 0.0;
    bool mDataPreparationFinishedCorrectly;
    MatrixXd mRefPos;
    MatrixXd mHpiPos;
    MatrixXd mRefResult;
    MatrixXd mHpiResult;
    QVector<int> vFreqs;
};

//=============================================================================================================

TestHpiFit::TestHpiFit()
: dErrorTrans(0.0003),
dErrorQuat(0.002),
dErrorTime(0.00000001),
dErrorAngle(0.1),
dErrorDetect(0.0),
mDataPreparationFinishedCorrectly(false)
{
}

//=============================================================================================================

void TestHpiFit::initTestCase()
{
    qInstallMessageHandler(ApplicationLogger::customLogWriter);
    qInfo() << "Error Translation" << dErrorTrans;
    qInfo() << "Error Quaternion" << dErrorQuat;
    QFile t_fileIn(QCoreApplication::applicationDirPath() + "/mne-cpp-test-data/MEG/sample/test_hpiFit_raw.fif");

    // Make sure test folder exists
    QFileInfo t_fileInInfo(t_fileIn);
    QDir().mkdir(t_fileInInfo.path());

    printf(">>>>>>>>>>>>>>>>>>>>>>>>> Read Raw and HPI fit >>>>>>>>>>>>>>>>>>>>>>>>>\n");

    // Setup for reading the raw data
    FiffRawData raw;
    raw = FiffRawData(t_fileIn);
    QSharedPointer<FiffInfo> pFiffInfo = QSharedPointer<FIFFLIB::FiffInfo>(new FiffInfo(raw.info));

    // Only filter MEG channels
    RowVectorXi picks = raw.info.pick_types(true, false, false);
    RowVectorXd cals;

    FiffCoordTrans devHeadT = pFiffInfo->dev_head_t;

    // Set up the reading parameters
    fiff_int_t from;
    fiff_int_t to;
    fiff_int_t first = raw.first_samp;
    fiff_int_t last = raw.last_samp;
    MatrixXd mData, mTimes;

    float quantum_sec = 0.2f;   //read and write in 200 ms junks
    fiff_int_t quantum = ceil(quantum_sec*pFiffInfo->sfreq);

    // Read Quaternion File from maxfilter and calculated movements/rotations with python
    IOUtils::read_eigen_matrix(mRefPos, QCoreApplication::applicationDirPath() + "/mne-cpp-test-data/Result/ref_hpiFit_pos.txt");
    IOUtils::read_eigen_matrix(mRefResult, QCoreApplication::applicationDirPath() + "/mne-cpp-test-data/Result/ref_angle_move.txt");

    mHpiResult = mRefResult;

    // define thresholds for big head movement detection
    float threshRot = 2.0f;
    float threshTrans = 0.002f;

    // Setup informations for HPI fit
    vFreqs = {154,158,161,166};
    QVector<double> vError;
    VectorXd vGoF;
    FiffDigPointSet fittedPointSet;
    Eigen::MatrixXd mProjectors = Eigen::MatrixXd::Identity(pFiffInfo->chs.size(), pFiffInfo->chs.size());
    QString sHPIResourceDir = QCoreApplication::applicationDirPath() + "/HPIFittingDebug";
    bool bDoDebug = true;

    HPIFit HPI = HPIFit(pFiffInfo, true);

    // bring frequencies into right order
    from = first + mRefPos(0,0)*pFiffInfo->sfreq;
    to = from + quantum;
    if(!raw.read_raw_segment(mData, mTimes, from, to)) {
        qCritical("error during read_raw_segment");
    }
    qInfo() << "[done]";

    qInfo() << "Order Frequecies: ...";
    HPI.findOrder(mData,
                  mProjectors,
                  pFiffInfo->dev_head_t,
                  vFreqs,
                  vError,
                  vGoF,
                  fittedPointSet,
                  pFiffInfo);
    qInfo() << "[done]";

    for(int i = 0; i < mRefPos.rows(); i++) {
        from = first + mRefPos(i,0)*pFiffInfo->sfreq;
        to = from + quantum;
        if (to > last) {
            to = last;
        }
        qInfo()  << "Reading...";
        if(!raw.read_raw_segment(mData, mTimes, from, to)) {
            qWarning("error during read_raw_segment\n");
        }

        qInfo()  << "HPI-Fit...";
        HPI.fitHPI(mData,
                   mProjectors,
                   pFiffInfo->dev_head_t,
                   vFreqs,
                   vError,
                   vGoF,
                   fittedPointSet,
                   pFiffInfo,
                   bDoDebug = 0,
                   sHPIResourceDir,
                   200,
                   1e-5f);
        qInfo() << "[done]\n";

        if(MNEMath::compareTransformation(devHeadT.trans, pFiffInfo->dev_head_t.trans, threshRot, threshTrans)) {
            mHpiResult(i,2) = 1;
        }

        HPIFit::storeHeadPosition(mRefPos(i,0), pFiffInfo->dev_head_t.trans, mHpiPos, vGoF, vError);
        mHpiResult(i,0) = devHeadT.translationTo(pFiffInfo->dev_head_t.trans);
        mHpiResult(i,1) = devHeadT.angleTo(pFiffInfo->dev_head_t.trans);

    }

    mDataPreparationFinishedCorrectly = true;
    // For debug: position file for HPIFit
//    UTILSLIB::IOUtils::write_eigen_matrix(mHpiPos, QCoreApplication::applicationDirPath() + "/MNE-sample-data/mHpiPos.txt");
}

//=============================================================================================================

void TestHpiFit::testDataPrepatationFinished()
{
    QVERIFY(mDataPreparationFinishedCorrectly);
}

//=============================================================================================================

void TestHpiFit::compareFrequencies()
{
    QVector<int> vFreqRef {166, 154, 161, 158};
    QVERIFY(vFreqRef == vFreqs);
}

//=============================================================================================================

void TestHpiFit::compareTranslation()
{
    RowVector3d vDiffTrans;
    vDiffTrans(0) = (mRefPos.col(4)-mHpiPos.col(4)).mean();
    vDiffTrans(1) = (mRefPos.col(5)-mHpiPos.col(5)).mean();
    vDiffTrans(2) = (mRefPos.col(6)-mHpiPos.col(6)).mean();
    qDebug() << "ErrorTrans x: " << std::abs(vDiffTrans(0));
    qDebug() << "ErrorTrans y: " << std::abs(vDiffTrans(1));
    qDebug() << "ErrorTrans z: " << std::abs(vDiffTrans(2));
    QVERIFY(std::abs(vDiffTrans(0)) < dErrorTrans);
    QVERIFY(std::abs(vDiffTrans(1)) < dErrorTrans);
    QVERIFY(std::abs(vDiffTrans(2)) < dErrorTrans);
}

//=============================================================================================================

void TestHpiFit::compareRotation()
{
    RowVector3d vDiffQuat;
    vDiffQuat(0) = (mRefPos.col(1)-mHpiPos.col(1)).mean();
    vDiffQuat(1) = (mRefPos.col(2)-mHpiPos.col(2)).mean();
    vDiffQuat(2) = (mRefPos.col(3)-mHpiPos.col(3)).mean();
    qDebug() << "ErrorQuat q1: " <<std::abs(vDiffQuat(0));
    qDebug() << "ErrorQuat q2: " <<std::abs(vDiffQuat(1));
    qDebug() << "ErrorQuat q3: " <<std::abs(vDiffQuat(2));
    QVERIFY(std::abs(vDiffQuat(0)) < dErrorQuat);
    QVERIFY(std::abs(vDiffQuat(1)) < dErrorQuat);
    QVERIFY(std::abs(vDiffQuat(2)) < dErrorQuat);
}

//=============================================================================================================

void TestHpiFit::compareMove()
{
    float fDiffMove = (mRefResult.col(0)-mHpiResult.col(0)).mean();
    fDiffMove = std::abs(fDiffMove);

    qDebug() << "DiffMove: [m]" << fDiffMove;
    QVERIFY(std::abs(fDiffMove) < dErrorTrans);
}

//=============================================================================================================

void TestHpiFit::compareAngle()
{
    float fDiffAngle = (mRefResult.col(1)-mRefResult.col(1)).mean();
    fDiffAngle = std::abs(fDiffAngle);

    qDebug() << "DiffAngle: [degree]" << fDiffAngle;
    QVERIFY(std::abs(fDiffAngle) < dErrorAngle);
}

//=============================================================================================================

void TestHpiFit::compareDetect()
{
    float fDiffCompare = (mRefResult.col(2)-mRefResult.col(2)).mean();
    fDiffCompare = std::abs(fDiffCompare);

    qDebug() << "DiffCompare: " << fDiffCompare;
    QVERIFY(std::abs(fDiffCompare) == dErrorDetect);
}

//=============================================================================================================

void TestHpiFit::compareTime()
{
    MatrixXd mDiff = MatrixXd::Zero(mRefPos.rows(),1);
    mDiff.col(0) = mRefPos.col(0)-mHpiPos.col(0);
    float fDiffTime = mDiff.col(0).mean();

    qDebug() << "ErrorTime: " << fDiffTime;
    QVERIFY(std::abs(fDiffTime) < dErrorTime);
}

//=============================================================================================================

void TestHpiFit::cleanupTestCase()
{
}

//=============================================================================================================
// MAIN
//=============================================================================================================

QTEST_GUILESS_MAIN(TestHpiFit)
#include "test_hpiFit.moc"
<commit_msg>delete unnecessary variable in test_hpi<commit_after>//=============================================================================================================
/**
 * @file     main.cpp
 * @author   Ruben Dörfel <ruben.doerfel@tu-ilmenau.de>;
 * @since    0.1.0
 * @date     February, 2020
 *
 * @section  LICENSE
 *
 * Copyright (C) 2020, Ruben Dörfel. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 * the following conditions are met:
 *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *       following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 *       the following disclaimer in the documentation and/or other materials provided with the distribution.
 *     * Neither the name of MNE-CPP authors nor the names of its contributors may be used
 *       to endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * @brief     Test for fitHpi function in inverse library.
 *
 */

//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include <utils/generics/applicationlogger.h>

#include <iostream>
#include <vector>
#include <math.h>

#include <fiff/fiff.h>
#include <fiff/fiff_info.h>
#include <fiff/fiff_dig_point_set.h>

#include <inverse/hpiFit/hpifit.h>
#include <inverse/hpiFit/hpifitdata.h>

#include <utils/ioutils.h>
#include <utils/mnemath.h>

#include <fwd/fwd_coil_set.h>

#include <Eigen/Dense>

//=============================================================================================================
// QT INCLUDES
//=============================================================================================================

#include <QtCore/QCoreApplication>
#include <QFile>
#include <QCommandLineParser>
#include <QDebug>
#include <QtTest>

//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================

using namespace FIFFLIB;
using namespace UTILSLIB;
using namespace INVERSELIB;
using namespace Eigen;

//=============================================================================================================
/**
 * DECLARE CLASS TestHpiFit
 *
 * @brief The TestHpiFit class provides hpi fit verifivcation tests
 *
 */
class TestHpiFit: public QObject
{
    Q_OBJECT

public:
    TestHpiFit();

private slots:
    void initTestCase();
    void compareFrequencies();
    void compareTranslation();
    void compareRotation();
    void compareAngle();
    void compareMove();
    void compareDetect();
    void compareTime();
    void cleanupTestCase();

private:
    double dErrorTrans = 0.0003;
    double dErrorQuat = 0.002;
    double dErrorTime = 0.00000001;
    double dErrorAngle = 0.1;
    double dErrorDetect = 0.0;
    MatrixXd mRefPos;
    MatrixXd mHpiPos;
    MatrixXd mRefResult;
    MatrixXd mHpiResult;
    QVector<int> vFreqs;
};

//=============================================================================================================

TestHpiFit::TestHpiFit()
: dErrorTrans(0.0003),
dErrorQuat(0.002),
dErrorTime(0.00000001),
dErrorAngle(0.1),
dErrorDetect(0.0)
{
}

//=============================================================================================================

void TestHpiFit::initTestCase()
{
    qInstallMessageHandler(ApplicationLogger::customLogWriter);
    qInfo() << "Error Translation" << dErrorTrans;
    qInfo() << "Error Quaternion" << dErrorQuat;
    QFile t_fileIn(QCoreApplication::applicationDirPath() + "/mne-cpp-test-data/MEG/sample/test_hpiFit_raw.fif");

    // Make sure test folder exists
    QFileInfo t_fileInInfo(t_fileIn);
    QDir().mkdir(t_fileInInfo.path());

    printf(">>>>>>>>>>>>>>>>>>>>>>>>> Read Raw and HPI fit >>>>>>>>>>>>>>>>>>>>>>>>>\n");

    // Setup for reading the raw data
    FiffRawData raw;
    raw = FiffRawData(t_fileIn);
    QSharedPointer<FiffInfo> pFiffInfo = QSharedPointer<FIFFLIB::FiffInfo>(new FiffInfo(raw.info));

    // Only filter MEG channels
    RowVectorXi picks = raw.info.pick_types(true, false, false);
    RowVectorXd cals;

    FiffCoordTrans devHeadT = pFiffInfo->dev_head_t;

    // Set up the reading parameters
    fiff_int_t from;
    fiff_int_t to;
    fiff_int_t first = raw.first_samp;
    fiff_int_t last = raw.last_samp;
    MatrixXd mData, mTimes;

    float quantum_sec = 0.2f;   //read and write in 200 ms junks
    fiff_int_t quantum = ceil(quantum_sec*pFiffInfo->sfreq);

    // Read Quaternion File from maxfilter and calculated movements/rotations with python
    IOUtils::read_eigen_matrix(mRefPos, QCoreApplication::applicationDirPath() + "/mne-cpp-test-data/Result/ref_hpiFit_pos.txt");
    IOUtils::read_eigen_matrix(mRefResult, QCoreApplication::applicationDirPath() + "/mne-cpp-test-data/Result/ref_angle_move.txt");

    mHpiResult = mRefResult;

    // define thresholds for big head movement detection
    float threshRot = 2.0f;
    float threshTrans = 0.002f;

    // Setup informations for HPI fit
    vFreqs = {154,158,161,166};
    QVector<double> vError;
    VectorXd vGoF;
    FiffDigPointSet fittedPointSet;
    Eigen::MatrixXd mProjectors = Eigen::MatrixXd::Identity(pFiffInfo->chs.size(), pFiffInfo->chs.size());
    QString sHPIResourceDir = QCoreApplication::applicationDirPath() + "/HPIFittingDebug";
    bool bDoDebug = true;

    HPIFit HPI = HPIFit(pFiffInfo, true);

    // bring frequencies into right order
    from = first + mRefPos(0,0)*pFiffInfo->sfreq;
    to = from + quantum;
    if(!raw.read_raw_segment(mData, mTimes, from, to)) {
        qCritical("error during read_raw_segment");
    }
    qInfo() << "[done]";

    qInfo() << "Order Frequecies: ...";
    HPI.findOrder(mData,
                  mProjectors,
                  pFiffInfo->dev_head_t,
                  vFreqs,
                  vError,
                  vGoF,
                  fittedPointSet,
                  pFiffInfo);
    qInfo() << "[done]";

    for(int i = 0; i < mRefPos.rows(); i++) {
        from = first + mRefPos(i,0)*pFiffInfo->sfreq;
        to = from + quantum;
        if (to > last) {
            to = last;
        }
        qInfo()  << "Reading...";
        if(!raw.read_raw_segment(mData, mTimes, from, to)) {
            qWarning("error during read_raw_segment\n");
        }

        qInfo()  << "HPI-Fit...";
        HPI.fitHPI(mData,
                   mProjectors,
                   pFiffInfo->dev_head_t,
                   vFreqs,
                   vError,
                   vGoF,
                   fittedPointSet,
                   pFiffInfo,
                   bDoDebug = 0,
                   sHPIResourceDir,
                   200,
                   1e-5f);
        qInfo() << "[done]\n";

        if(MNEMath::compareTransformation(devHeadT.trans, pFiffInfo->dev_head_t.trans, threshRot, threshTrans)) {
            mHpiResult(i,2) = 1;
        }

        HPIFit::storeHeadPosition(mRefPos(i,0), pFiffInfo->dev_head_t.trans, mHpiPos, vGoF, vError);
        mHpiResult(i,0) = devHeadT.translationTo(pFiffInfo->dev_head_t.trans);
        mHpiResult(i,1) = devHeadT.angleTo(pFiffInfo->dev_head_t.trans);

    }
    // For debug: position file for HPIFit
//    UTILSLIB::IOUtils::write_eigen_matrix(mHpiPos, QCoreApplication::applicationDirPath() + "/MNE-sample-data/mHpiPos.txt");
}

//=============================================================================================================

void TestHpiFit::compareFrequencies()
{
    QVector<int> vFreqRef {166, 154, 161, 158};
    QVERIFY(vFreqRef == vFreqs);
}

//=============================================================================================================

void TestHpiFit::compareTranslation()
{
    RowVector3d vDiffTrans;
    vDiffTrans(0) = (mRefPos.col(4)-mHpiPos.col(4)).mean();
    vDiffTrans(1) = (mRefPos.col(5)-mHpiPos.col(5)).mean();
    vDiffTrans(2) = (mRefPos.col(6)-mHpiPos.col(6)).mean();
    qDebug() << "ErrorTrans x: " << std::abs(vDiffTrans(0));
    qDebug() << "ErrorTrans y: " << std::abs(vDiffTrans(1));
    qDebug() << "ErrorTrans z: " << std::abs(vDiffTrans(2));
    QVERIFY(std::abs(vDiffTrans(0)) < dErrorTrans);
    QVERIFY(std::abs(vDiffTrans(1)) < dErrorTrans);
    QVERIFY(std::abs(vDiffTrans(2)) < dErrorTrans);
}

//=============================================================================================================

void TestHpiFit::compareRotation()
{
    RowVector3d vDiffQuat;
    vDiffQuat(0) = (mRefPos.col(1)-mHpiPos.col(1)).mean();
    vDiffQuat(1) = (mRefPos.col(2)-mHpiPos.col(2)).mean();
    vDiffQuat(2) = (mRefPos.col(3)-mHpiPos.col(3)).mean();
    qDebug() << "ErrorQuat q1: " <<std::abs(vDiffQuat(0));
    qDebug() << "ErrorQuat q2: " <<std::abs(vDiffQuat(1));
    qDebug() << "ErrorQuat q3: " <<std::abs(vDiffQuat(2));
    QVERIFY(std::abs(vDiffQuat(0)) < dErrorQuat);
    QVERIFY(std::abs(vDiffQuat(1)) < dErrorQuat);
    QVERIFY(std::abs(vDiffQuat(2)) < dErrorQuat);
}

//=============================================================================================================

void TestHpiFit::compareMove()
{
    float fDiffMove = (mRefResult.col(0)-mHpiResult.col(0)).mean();
    fDiffMove = std::abs(fDiffMove);

    qDebug() << "DiffMove: [m]" << fDiffMove;
    QVERIFY(std::abs(fDiffMove) < dErrorTrans);
}

//=============================================================================================================

void TestHpiFit::compareAngle()
{
    float fDiffAngle = (mRefResult.col(1)-mRefResult.col(1)).mean();
    fDiffAngle = std::abs(fDiffAngle);

    qDebug() << "DiffAngle: [degree]" << fDiffAngle;
    QVERIFY(std::abs(fDiffAngle) < dErrorAngle);
}

//=============================================================================================================

void TestHpiFit::compareDetect()
{
    float fDiffCompare = (mRefResult.col(2)-mRefResult.col(2)).mean();
    fDiffCompare = std::abs(fDiffCompare);

    qDebug() << "DiffCompare: " << fDiffCompare;
    QVERIFY(std::abs(fDiffCompare) == dErrorDetect);
}

//=============================================================================================================

void TestHpiFit::compareTime()
{
    MatrixXd mDiff = MatrixXd::Zero(mRefPos.rows(),1);
    mDiff.col(0) = mRefPos.col(0)-mHpiPos.col(0);
    float fDiffTime = mDiff.col(0).mean();

    qDebug() << "ErrorTime: " << fDiffTime;
    QVERIFY(std::abs(fDiffTime) < dErrorTime);
}

//=============================================================================================================

void TestHpiFit::cleanupTestCase()
{
}

//=============================================================================================================
// MAIN
//=============================================================================================================

QTEST_GUILESS_MAIN(TestHpiFit)
#include "test_hpiFit.moc"
<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// transaction_test.cpp
//
// Identification: tests/concurrency/transaction_test.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "harness.h"
#include "concurrency/transaction_tests_util.h"

namespace peloton {

namespace test {

//===--------------------------------------------------------------------===//
// Transaction Tests
//===--------------------------------------------------------------------===//

class TransactionTests : public PelotonTest {};

std::vector<ConcurrencyType> TEST_TYPES = {
    // CONCURRENCY_TYPE_OCC
    CONCURRENCY_TYPE_2PL};

void TransactionTest(concurrency::TransactionManager *txn_manager) {
  uint64_t thread_id = TestingHarness::GetInstance().GetThreadId();

  for (oid_t txn_itr = 1; txn_itr <= 50; txn_itr++) {
    txn_manager->BeginTransaction();
    if (thread_id % 2 == 0) {
      std::chrono::microseconds sleep_time(1);
      std::this_thread::sleep_for(sleep_time);
    }

    if (txn_itr % 25 != 0) {
      txn_manager->CommitTransaction();
    } else {
      txn_manager->AbortTransaction();
    }
  }
}

void DirtyWriteTest(ConcurrencyType test_type) {
  auto &txn_manager =
      concurrency::TransactionManagerFactory::GetInstance(test_type);
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    // T1 updates (0, ?) to (0, 1)
    // T2 updates (0, ?) to (0, 2)
    // T1 commits
    // T2 commits
    scheduler.AddUpdate(0, 0, 1);
    scheduler.AddUpdate(1, 0, 2);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();
    auto &schedules = scheduler.schedules;

    // T1 and T2 can't both succeed
    EXPECT_FALSE(schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_SUCCESS);
    // For MVCC, actually one and only one T should succeed?
    EXPECT_TRUE((schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_ABORTED) ||
                (schedules[0].txn_result == RESULT_ABORTED &&
                 schedules[1].txn_result == RESULT_SUCCESS));
    schedules.clear();
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);

    scheduler.AddUpdate(0, 0, 1);
    scheduler.AddUpdate(1, 0, 2);
    scheduler.AddCommit(1);
    scheduler.AddCommit(0);

    scheduler.Run();
    auto &schedules = scheduler.schedules;

    // T1 and T2 can't both succeed
    EXPECT_FALSE(schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_SUCCESS);
    // For MVCC, actually one and only one T should succeed?
    EXPECT_TRUE((schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_ABORTED) ||
                (schedules[0].txn_result == RESULT_ABORTED &&
                 schedules[1].txn_result == RESULT_SUCCESS));
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    // T0 delete (0, ?)
    // T1 update (0, ?) to (0, 3)
    // T0 commit
    // T1 commit
    scheduler.AddDelete(0, 0);
    scheduler.AddUpdate(1, 0, 3);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();
    auto &schedules = scheduler.schedules;

    // T1 and T2 can't both succeed
    EXPECT_FALSE(schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_SUCCESS);
    // For MVCC, actually one and only one T should succeed?
    EXPECT_TRUE((schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_ABORTED) ||
                (schedules[0].txn_result == RESULT_ABORTED &&
                 schedules[1].txn_result == RESULT_SUCCESS));
    schedules.clear();
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    // T0 delete (1, ?)
    // T1 delete (1, ?)
    // T0 commit
    // T1 commit
    scheduler.AddDelete(0, 1);
    scheduler.AddDelete(1, 1);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();
    auto &schedules = scheduler.schedules;

    // T1 and T2 can't both succeed
    EXPECT_FALSE(schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_SUCCESS);
    // For MVCC, actually one and only one T should succeed?
    EXPECT_TRUE((schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_ABORTED) ||
                (schedules[0].txn_result == RESULT_ABORTED &&
                 schedules[1].txn_result == RESULT_SUCCESS));
    schedules.clear();
  }
}

void DirtyReadTest(ConcurrencyType TEST_TYPE) {
  auto &txn_manager =
      concurrency::TransactionManagerFactory::GetInstance(TEST_TYPE);
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);

    // T1 updates (0, ?) to (0, 1)
    // T2 reads (0, ?)
    // T1 commit
    // T2 commit
    scheduler.AddUpdate(0, 0, 1);
    scheduler.AddRead(1, 0);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();

    EXPECT_FALSE(RESULT_ABORTED == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);

    // T1 updates (0, ?) to (0, 1)
    // T2 reads (0, ?)
    // T2 commit
    // T1 commit
    scheduler.AddUpdate(0, 0, 1);
    scheduler.AddRead(1, 0);
    scheduler.AddCommit(1);
    scheduler.AddCommit(0);

    scheduler.Run();

    EXPECT_FALSE(RESULT_ABORTED == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);

    // T0 delete (0, ?)
    // T1 read (0, ?)
    // T0 commit
    // T1 commit
    scheduler.AddDelete(0, 0);
    scheduler.AddRead(1, 0);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();

    EXPECT_FALSE(RESULT_ABORTED == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }
}

void FuzzyReadTest(ConcurrencyType TEST_TYPE) {
  auto &txn_manager =
      concurrency::TransactionManagerFactory::GetInstance(TEST_TYPE);
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  {
    // T0 read 0
    // T1 update (0, 0) to (0, 1)
    // T1 commit
    // T0 commit
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddRead(0, 0);
    scheduler.AddUpdate(1, 0, 1);
    scheduler.AddCommit(1);
    scheduler.AddCommit(0);

    scheduler.Run();

    LOG_TRACE("%lu", scheduler.schedules.size());
    EXPECT_FALSE(RESULT_ABORTED == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }

  {
    // T0 read 0
    // T1 update (0, 0) to (0, 1)
    // T0 commit
    // T1 commit
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddRead(0, 0);
    scheduler.AddUpdate(1, 0, 1);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();

    EXPECT_FALSE(RESULT_ABORTED == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }

  {
    // T0 read 0
    // T1 delete 0
    // T0 commit
    // T1 commit
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddRead(0, 0);
    scheduler.AddDelete(1, 0);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();
    EXPECT_FALSE(RESULT_ABORTED == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }
}

void PhantomTest(ConcurrencyType TEST_TYPE) {
  auto &txn_manager =
      concurrency::TransactionManagerFactory::GetInstance(TEST_TYPE);
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddScan(0, 0);
    scheduler.AddInsert(1, 5, 0);
    scheduler.AddCommit(1);
    scheduler.AddCommit(0);

    scheduler.Run();
    EXPECT_EQ(RESULT_ABORTED, scheduler.schedules[0].txn_result);
    EXPECT_EQ(RESULT_SUCCESS, scheduler.schedules[1].txn_result);
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddScan(0, 0);
    scheduler.AddDelete(1, 4);
    scheduler.AddCommit(1);
    scheduler.AddCommit(0);

    scheduler.Run();
    EXPECT_EQ(RESULT_ABORTED, scheduler.schedules[0].txn_result);
    EXPECT_EQ(RESULT_SUCCESS, scheduler.schedules[1].txn_result);
  }
}

void WriteSkewTest(ConcurrencyType TEST_TYPE) {
  auto &txn_manager =
      concurrency::TransactionManagerFactory::GetInstance(TEST_TYPE);
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddRead(0, 0);
    scheduler.AddUpdate(0, 1, 1);
    scheduler.AddRead(1, 0);
    scheduler.AddUpdate(1, 1, 2);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();

    // Can't all success
    EXPECT_FALSE(RESULT_SUCCESS == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddRead(0, 0);
    scheduler.AddUpdate(0, 1, 1);
    scheduler.AddRead(1, 0);
    scheduler.AddCommit(0);
    scheduler.AddUpdate(1, 1, 2);
    scheduler.AddCommit(1);

    scheduler.Run();

    // First txn should success
    EXPECT_TRUE(RESULT_SUCCESS == scheduler.schedules[0].txn_result &&
                RESULT_ABORTED == scheduler.schedules[1].txn_result);
  }
}

void ReadSkewTest(ConcurrencyType test_type) {
  auto &txn_manager =
      concurrency::TransactionManagerFactory::GetInstance(test_type);
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());
  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddRead(0, 0);
    scheduler.AddUpdate(1, 0, 1);
    scheduler.AddUpdate(1, 1, 1);
    scheduler.AddCommit(1);
    scheduler.AddRead(0, 1);
    scheduler.AddCommit(0);

    scheduler.Run();

    EXPECT_FALSE(RESULT_SUCCESS == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }
}

TEST_F(TransactionTests, TransactionTest) {
  for (auto test_type : TEST_TYPES) {
    auto &txn_manager =
        concurrency::TransactionManagerFactory::GetInstance(test_type);

    LaunchParallelTest(8, TransactionTest, &txn_manager);

    std::cout << "next Commit Id :: " << txn_manager.GetNextCommitId() << "\n";
  }
}

TEST_F(TransactionTests, AbortTest) {
  for (auto test_type : TEST_TYPES) {
    auto &txn_manager =
        concurrency::TransactionManagerFactory::GetInstance(test_type);
    std::unique_ptr<storage::DataTable> table(
        TransactionTestsUtil::CreateTable());
    {
      TransactionScheduler scheduler(2, table.get(), &txn_manager);
      scheduler.AddUpdate(0, 0, 100);
      scheduler.AddAbort(0);
      scheduler.AddRead(1, 0);
      scheduler.AddCommit(1);

      scheduler.Run();

      EXPECT_EQ(RESULT_ABORTED, scheduler.schedules[0].txn_result);
      EXPECT_EQ(RESULT_SUCCESS, scheduler.schedules[1].txn_result);
      EXPECT_EQ(0, scheduler.schedules[1].results[0]);
    }

    {
      TransactionScheduler scheduler(2, table.get(), &txn_manager);
      scheduler.AddInsert(0, 100, 0);
      scheduler.AddAbort(0);
      scheduler.AddRead(1, 100);
      scheduler.AddCommit(1);

      scheduler.Run();
      EXPECT_EQ(RESULT_ABORTED, scheduler.schedules[0].txn_result);
      EXPECT_EQ(RESULT_SUCCESS, scheduler.schedules[1].txn_result);
      EXPECT_EQ(-1, scheduler.schedules[1].results[0]);
    }
  }
}

TEST_F(TransactionTests, SerializableTest) {
  for (auto test_type : TEST_TYPES) {
    DirtyWriteTest(test_type);
    DirtyReadTest(test_type);
    FuzzyReadTest(test_type);
    WriteSkewTest(test_type);
    ReadSkewTest(test_type);
    //  PhantomTes();
  }
}

}  // End test namespace
}  // End peloton namespace
<commit_msg>txn test is too strict<commit_after>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// transaction_test.cpp
//
// Identification: tests/concurrency/transaction_test.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "harness.h"
#include "concurrency/transaction_tests_util.h"

namespace peloton {

namespace test {

//===--------------------------------------------------------------------===//
// Transaction Tests
//===--------------------------------------------------------------------===//

class TransactionTests : public PelotonTest {};

std::vector<ConcurrencyType> TEST_TYPES = {
    // CONCURRENCY_TYPE_OCC
    CONCURRENCY_TYPE_2PL};

void TransactionTest(concurrency::TransactionManager *txn_manager) {
  uint64_t thread_id = TestingHarness::GetInstance().GetThreadId();

  for (oid_t txn_itr = 1; txn_itr <= 50; txn_itr++) {
    txn_manager->BeginTransaction();
    if (thread_id % 2 == 0) {
      std::chrono::microseconds sleep_time(1);
      std::this_thread::sleep_for(sleep_time);
    }

    if (txn_itr % 25 != 0) {
      txn_manager->CommitTransaction();
    } else {
      txn_manager->AbortTransaction();
    }
  }
}

void DirtyWriteTest(ConcurrencyType test_type) {
  auto &txn_manager =
      concurrency::TransactionManagerFactory::GetInstance(test_type);
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    // T1 updates (0, ?) to (0, 1)
    // T2 updates (0, ?) to (0, 2)
    // T1 commits
    // T2 commits
    scheduler.AddUpdate(0, 0, 1);
    scheduler.AddUpdate(1, 0, 2);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();
    auto &schedules = scheduler.schedules;

    // T1 and T2 can't both succeed
    EXPECT_FALSE(schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_SUCCESS);
    // For MVCC, actually one and only one T should succeed?
    EXPECT_TRUE((schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_ABORTED) ||
                (schedules[0].txn_result == RESULT_ABORTED &&
                 schedules[1].txn_result == RESULT_SUCCESS));
    schedules.clear();
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);

    scheduler.AddUpdate(0, 0, 1);
    scheduler.AddUpdate(1, 0, 2);
    scheduler.AddCommit(1);
    scheduler.AddCommit(0);

    scheduler.Run();
    auto &schedules = scheduler.schedules;

    // T1 and T2 can't both succeed
    EXPECT_FALSE(schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_SUCCESS);
    // For MVCC, actually one and only one T should succeed?
    EXPECT_TRUE((schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_ABORTED) ||
                (schedules[0].txn_result == RESULT_ABORTED &&
                 schedules[1].txn_result == RESULT_SUCCESS));
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    // T0 delete (0, ?)
    // T1 update (0, ?) to (0, 3)
    // T0 commit
    // T1 commit
    scheduler.AddDelete(0, 0);
    scheduler.AddUpdate(1, 0, 3);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();
    auto &schedules = scheduler.schedules;

    // T1 and T2 can't both succeed
    EXPECT_FALSE(schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_SUCCESS);
    // For MVCC, actually one and only one T should succeed?
    EXPECT_TRUE((schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_ABORTED) ||
                (schedules[0].txn_result == RESULT_ABORTED &&
                 schedules[1].txn_result == RESULT_SUCCESS));
    schedules.clear();
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    // T0 delete (1, ?)
    // T1 delete (1, ?)
    // T0 commit
    // T1 commit
    scheduler.AddDelete(0, 1);
    scheduler.AddDelete(1, 1);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();
    auto &schedules = scheduler.schedules;

    // T1 and T2 can't both succeed
    EXPECT_FALSE(schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_SUCCESS);
    // For MVCC, actually one and only one T should succeed?
    EXPECT_TRUE((schedules[0].txn_result == RESULT_SUCCESS &&
                 schedules[1].txn_result == RESULT_ABORTED) ||
                (schedules[0].txn_result == RESULT_ABORTED &&
                 schedules[1].txn_result == RESULT_SUCCESS));
    schedules.clear();
  }
}

void DirtyReadTest(ConcurrencyType TEST_TYPE) {
  auto &txn_manager =
      concurrency::TransactionManagerFactory::GetInstance(TEST_TYPE);
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);

    // T1 updates (0, ?) to (0, 1)
    // T2 reads (0, ?)
    // T1 commit
    // T2 commit
    scheduler.AddUpdate(0, 0, 1);
    scheduler.AddRead(1, 0);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();

    EXPECT_FALSE(RESULT_ABORTED == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);

    // T1 updates (0, ?) to (0, 1)
    // T2 reads (0, ?)
    // T2 commit
    // T1 commit
    scheduler.AddUpdate(0, 0, 1);
    scheduler.AddRead(1, 0);
    scheduler.AddCommit(1);
    scheduler.AddCommit(0);

    scheduler.Run();

    EXPECT_FALSE(RESULT_ABORTED == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);

    // T0 delete (0, ?)
    // T1 read (0, ?)
    // T0 commit
    // T1 commit
    scheduler.AddDelete(0, 0);
    scheduler.AddRead(1, 0);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();

    EXPECT_FALSE(RESULT_ABORTED == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }
}

void FuzzyReadTest(ConcurrencyType TEST_TYPE) {
  auto &txn_manager =
      concurrency::TransactionManagerFactory::GetInstance(TEST_TYPE);
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  {
    // T0 read 0
    // T1 update (0, 0) to (0, 1)
    // T1 commit
    // T0 commit
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddRead(0, 0);
    scheduler.AddUpdate(1, 0, 1);
    scheduler.AddCommit(1);
    scheduler.AddCommit(0);

    scheduler.Run();

    LOG_TRACE("%lu", scheduler.schedules.size());
    EXPECT_FALSE(RESULT_SUCCESS == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }

  {
    // T0 read 0
    // T1 update (0, 0) to (0, 1)
    // T0 commit
    // T1 commit
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddRead(0, 0);
    scheduler.AddUpdate(1, 0, 1);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();

    EXPECT_FALSE(RESULT_SUCCESS == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }

  {
    // T0 read 0
    // T1 delete 0
    // T0 commit
    // T1 commit
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddRead(0, 0);
    scheduler.AddDelete(1, 0);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();
    EXPECT_FALSE(RESULT_SUCCESS == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }
}

void PhantomTest(ConcurrencyType TEST_TYPE) {
  auto &txn_manager =
      concurrency::TransactionManagerFactory::GetInstance(TEST_TYPE);
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddScan(0, 0);
    scheduler.AddInsert(1, 5, 0);
    scheduler.AddCommit(1);
    scheduler.AddCommit(0);

    scheduler.Run();
    EXPECT_EQ(RESULT_ABORTED, scheduler.schedules[0].txn_result);
    EXPECT_EQ(RESULT_SUCCESS, scheduler.schedules[1].txn_result);
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddScan(0, 0);
    scheduler.AddDelete(1, 4);
    scheduler.AddCommit(1);
    scheduler.AddCommit(0);

    scheduler.Run();
    EXPECT_EQ(RESULT_ABORTED, scheduler.schedules[0].txn_result);
    EXPECT_EQ(RESULT_SUCCESS, scheduler.schedules[1].txn_result);
  }
}

void WriteSkewTest(ConcurrencyType TEST_TYPE) {
  auto &txn_manager =
      concurrency::TransactionManagerFactory::GetInstance(TEST_TYPE);
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddRead(0, 0);
    scheduler.AddUpdate(0, 1, 1);
    scheduler.AddRead(1, 0);
    scheduler.AddUpdate(1, 1, 2);
    scheduler.AddCommit(0);
    scheduler.AddCommit(1);

    scheduler.Run();

    // Can't all success
    EXPECT_FALSE(RESULT_SUCCESS == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }

  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddRead(0, 0);
    scheduler.AddUpdate(0, 1, 1);
    scheduler.AddRead(1, 0);
    scheduler.AddCommit(0);
    scheduler.AddUpdate(1, 1, 2);
    scheduler.AddCommit(1);

    scheduler.Run();

    // First txn should success
    EXPECT_TRUE(RESULT_SUCCESS == scheduler.schedules[0].txn_result &&
                RESULT_ABORTED == scheduler.schedules[1].txn_result);
  }
}

void ReadSkewTest(ConcurrencyType test_type) {
  auto &txn_manager =
      concurrency::TransactionManagerFactory::GetInstance(test_type);
  std::unique_ptr<storage::DataTable> table(
      TransactionTestsUtil::CreateTable());
  {
    TransactionScheduler scheduler(2, table.get(), &txn_manager);
    scheduler.AddRead(0, 0);
    scheduler.AddUpdate(1, 0, 1);
    scheduler.AddUpdate(1, 1, 1);
    scheduler.AddCommit(1);
    scheduler.AddRead(0, 1);
    scheduler.AddCommit(0);

    scheduler.Run();

    EXPECT_FALSE(RESULT_SUCCESS == scheduler.schedules[0].txn_result &&
                 RESULT_SUCCESS == scheduler.schedules[1].txn_result);
  }
}

TEST_F(TransactionTests, TransactionTest) {
  for (auto test_type : TEST_TYPES) {
    auto &txn_manager =
        concurrency::TransactionManagerFactory::GetInstance(test_type);

    LaunchParallelTest(8, TransactionTest, &txn_manager);

    std::cout << "next Commit Id :: " << txn_manager.GetNextCommitId() << "\n";
  }
}

TEST_F(TransactionTests, AbortTest) {
  for (auto test_type : TEST_TYPES) {
    auto &txn_manager =
        concurrency::TransactionManagerFactory::GetInstance(test_type);
    std::unique_ptr<storage::DataTable> table(
        TransactionTestsUtil::CreateTable());
    {
      TransactionScheduler scheduler(2, table.get(), &txn_manager);
      scheduler.AddUpdate(0, 0, 100);
      scheduler.AddAbort(0);
      scheduler.AddRead(1, 0);
      scheduler.AddCommit(1);

      scheduler.Run();

      EXPECT_EQ(RESULT_ABORTED, scheduler.schedules[0].txn_result);
      EXPECT_EQ(RESULT_SUCCESS, scheduler.schedules[1].txn_result);
      EXPECT_EQ(0, scheduler.schedules[1].results[0]);
    }

    {
      TransactionScheduler scheduler(2, table.get(), &txn_manager);
      scheduler.AddInsert(0, 100, 0);
      scheduler.AddAbort(0);
      scheduler.AddRead(1, 100);
      scheduler.AddCommit(1);

      scheduler.Run();
      EXPECT_EQ(RESULT_ABORTED, scheduler.schedules[0].txn_result);
      EXPECT_EQ(RESULT_SUCCESS, scheduler.schedules[1].txn_result);
      EXPECT_EQ(-1, scheduler.schedules[1].results[0]);
    }
  }
}

TEST_F(TransactionTests, SerializableTest) {
  for (auto test_type : TEST_TYPES) {
    DirtyWriteTest(test_type);
    DirtyReadTest(test_type);
    FuzzyReadTest(test_type);
    WriteSkewTest(test_type);
    ReadSkewTest(test_type);
    //  PhantomTes();
  }
}

}  // End test namespace
}  // End peloton namespace
<|endoftext|>


<commit_before>/*
* Copyright (c) 2008-2018 the MRtrix3 contributors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/
*
* MRtrix3 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* For more details, see http://www.mrtrix.org/
*/
#include "gui/mrview/sync/syncmanager.h"
#include "gui/mrview/window.h"
#include <iostream>
#include <vector>
#include <memory> //shared_ptr
#include "gui/mrview/sync/enums.h"

namespace MR
{
  namespace GUI
  {
    namespace MRView
    {
      namespace Sync
      {
        SyncManager::SyncManager() : QObject(0)
        {
          try
          {
            ips = new InterprocessCommunicator();//will throw exception if it fails to set up a server
            connect(ips, SIGNAL(SyncDataReceived(std::vector<std::shared_ptr<QByteArray>>)), this, SLOT(OnIPSDataReceived(std::vector<std::shared_ptr<QByteArray>>)));
          }
          catch (...)
          {
            ips = 0;
            WARN("Sync set up failed.");
          }
        }

        /**
        * Returns true if this is in a state which is not appropriate to connect a window
        */
        bool SyncManager::GetInErrorState()
        {
          return ips == 0;
        }

        /**
        * Sets the window to connect to. Code currently assumes this only occurs once. Also check GetInErrorState() before calling
        */
        void SyncManager::SetWindow(MR::GUI::MRView::Window* wind)
        {
          if (GetInErrorState())
          {
            throw Exception("Attempt to set window while in an error state");
          }
          win = wind;
          connect(win, SIGNAL(focusChanged()), this, SLOT(OnWindowFocusChanged()));
        }

        /**
        * Receives a signal from window that the focus has changed
        */
        void SyncManager::OnWindowFocusChanged()
        {
          if (win->sync_focus_on())
          {
            Eigen::Vector3f foc = win->focus();
            SendData(DataKey::WindowFocus, ToQByteArray(foc));
          }
        }

        /**
        * Sends a signal to other processes to sync to a given key/value
        */
        bool SyncManager::SendData(DataKey code, QByteArray dat)
        {
          QByteArray data;
          char codeAsChar[4];
          InterprocessCommunicator::Int32ToChar(codeAsChar, (int)code);
          data.insert(0, codeAsChar, 4);
          data.insert(4, dat, dat.size());

          return ips->SendData(data);
        }

        /**
        * Receives a signal from another process that a value to sync has changed
        */
        void SyncManager::OnIPSDataReceived(std::vector<std::shared_ptr<QByteArray>> all_messages)
        {
          //WARNING This code assumes that the order of syncing operations does not matter

          //We have a list of messages found
          //Categorise these. Only keep the last value sent for each message type, or we will change to an old value and then update other processes to this old value
          std::shared_ptr<QByteArray> winFocus = 0;

          for (size_t i = 0; i < all_messages.size(); i++)
          {
            std::shared_ptr<QByteArray> data = all_messages[i];

            if (data->size() < 4)
            {
              DEBUG("Bad data received to syncmanager: too short");
              continue;
            }

            int idOfDataEntry = InterprocessCommunicator::CharTo32bitNum(data->data());

            switch (idOfDataEntry)
            {
              case (int)DataKey::WindowFocus:
              {
                //This message has window focus information to sync with
                winFocus = data;
                break;
              }
              default:
              {
                DEBUG("Unknown data key received: " + idOfDataEntry);
                break;
              }
            }
          }

          if (winFocus && win->sync_focus_on())
          {
            //We received 1+ signals to change our window focus

            unsigned int offset = 4;//we have already read 4 bytes, above
            //Read three single point floats
            if (winFocus->size() != (int)(offset + 12)) //cast to int to avoid compiler warning
            {
              DEBUG("Bad data received to sync manager: wrong length (window focus)");
            }
            else
            {
              Eigen::Vector3f vec = FromQByteArray(*winFocus, offset);

              //Check if already set to this value - Basic OOP: don't trust window to check things are changed before emitting a signal that the value has changed!
              Eigen::Vector3f win_vec = win->focus();
              if (win_vec[0] != vec[0] || win_vec[1] != vec[1] || win_vec[2] != vec[2])
              {
                //Send to window
                win->set_focus(vec);
              }
            }
          }

          //Redraw the window.
          win->updateGL();
        }

        /**
        * Serialises a Vector3f as a QByteArray
        */
        QByteArray SyncManager::ToQByteArray(Eigen::Vector3f data)
        {
          char a[12];
          memcpy(a, &data, 12);
          QByteArray q;
          q.insert(0, a, 12); //don't use the constructor; it ignores any data after hitting a \0
          return q;
        }
        /**
        * Deserialises a Vector3f from a QByteArray
        */
        Eigen::Vector3f SyncManager::FromQByteArray(QByteArray data, unsigned int offset)
        {
          Eigen::Vector3f read;
          memcpy(&read, data.data() + offset, 12);
          return read;
        }

      }
    }
  }
}<commit_msg>Fixed compiler warning appearing on OSX. Tested on Ubuntu 12.04 (Virtual Box) + Qt 4.8, Windows 10 + Qt 5.9.2, and OSX Darwin + Qt 5.10.1<commit_after>/*
* Copyright (c) 2008-2018 the MRtrix3 contributors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/
*
* MRtrix3 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* For more details, see http://www.mrtrix.org/
*/
#include "gui/mrview/sync/syncmanager.h"
#include "gui/mrview/window.h"
#include <iostream>
#include <vector>
#include <memory> //shared_ptr
#include "gui/mrview/sync/enums.h"

namespace MR
{
  namespace GUI
  {
    namespace MRView
    {
      namespace Sync
      {
        SyncManager::SyncManager() : QObject(0)
        {
          try
          {
            ips = new InterprocessCommunicator();//will throw exception if it fails to set up a server
            connect(ips, SIGNAL(SyncDataReceived(std::vector<std::shared_ptr<QByteArray>>)), this, SLOT(OnIPSDataReceived(std::vector<std::shared_ptr<QByteArray>>)));
          }
          catch (...)
          {
            ips = 0;
            WARN("Sync set up failed.");
          }
        }

        /**
        * Returns true if this is in a state which is not appropriate to connect a window
        */
        bool SyncManager::GetInErrorState()
        {
          return ips == 0;
        }

        /**
        * Sets the window to connect to. Code currently assumes this only occurs once. Also check GetInErrorState() before calling
        */
        void SyncManager::SetWindow(MR::GUI::MRView::Window* wind)
        {
          if (GetInErrorState())
          {
            throw Exception("Attempt to set window while in an error state");
          }
          win = wind;
          connect(win, SIGNAL(focusChanged()), this, SLOT(OnWindowFocusChanged()));
        }

        /**
        * Receives a signal from window that the focus has changed
        */
        void SyncManager::OnWindowFocusChanged()
        {
          if (win->sync_focus_on())
          {
            Eigen::Vector3f foc = win->focus();
            SendData(DataKey::WindowFocus, ToQByteArray(foc));
          }
        }

        /**
        * Sends a signal to other processes to sync to a given key/value
        */
        bool SyncManager::SendData(DataKey code, QByteArray dat)
        {
          QByteArray data;
          char codeAsChar[4];
          InterprocessCommunicator::Int32ToChar(codeAsChar, (int)code);
          data.insert(0, codeAsChar, 4);
          data.insert(4, dat, dat.size());

          return ips->SendData(data);
        }

        /**
        * Receives a signal from another process that a value to sync has changed
        */
        void SyncManager::OnIPSDataReceived(std::vector<std::shared_ptr<QByteArray>> all_messages)
        {
          //WARNING This code assumes that the order of syncing operations does not matter

          //We have a list of messages found
          //Categorise these. Only keep the last value sent for each message type, or we will change to an old value and then update other processes to this old value
          std::shared_ptr<QByteArray> winFocus = 0;

          for (size_t i = 0; i < all_messages.size(); i++)
          {
            std::shared_ptr<QByteArray> data = all_messages[i];

            if (data->size() < 4)
            {
              DEBUG("Bad data received to syncmanager: too short");
              continue;
            }

            int idOfDataEntry = InterprocessCommunicator::CharTo32bitNum(data->data());

            switch (idOfDataEntry)
            {
              case (int)DataKey::WindowFocus:
              {
                //This message has window focus information to sync with
                winFocus = data;
                break;
              }
              default:
              {
                DEBUG("Unknown data key received: " + std::to_string(idOfDataEntry));
                break;
              }
            }
          }

          if (winFocus && win->sync_focus_on())
          {
            //We received 1+ signals to change our window focus

            unsigned int offset = 4;//we have already read 4 bytes, above
            //Read three single point floats
            if (winFocus->size() != (int)(offset + 12)) //cast to int to avoid compiler warning
            {
              DEBUG("Bad data received to sync manager: wrong length (window focus)");
            }
            else
            {
              Eigen::Vector3f vec = FromQByteArray(*winFocus, offset);

              //Check if already set to this value - Basic OOP: don't trust window to check things are changed before emitting a signal that the value has changed!
              Eigen::Vector3f win_vec = win->focus();
              if (win_vec[0] != vec[0] || win_vec[1] != vec[1] || win_vec[2] != vec[2])
              {
                //Send to window
                win->set_focus(vec);
              }
            }
          }

          //Redraw the window.
          win->updateGL();
        }

        /**
        * Serialises a Vector3f as a QByteArray
        */
        QByteArray SyncManager::ToQByteArray(Eigen::Vector3f data)
        {
          char a[12];
          memcpy(a, &data, 12);
          QByteArray q;
          q.insert(0, a, 12); //don't use the constructor; it ignores any data after hitting a \0
          return q;
        }
        /**
        * Deserialises a Vector3f from a QByteArray
        */
        Eigen::Vector3f SyncManager::FromQByteArray(QByteArray data, unsigned int offset)
        {
          Eigen::Vector3f read;
          memcpy(&read, data.data() + offset, 12);
          return read;
        }

      }
    }
  }
}<|endoftext|>