<commit_before>#include "thread_pool.h"
#include <gtest/gtest.h>
#include <atomic>

// We don't (yet) use fake time for this
// because the threads actually need to run.
//#ifdef FAKE_TIME
//#define Time FakeTime
//#endif

using namespace dronecode_sdk;
using namespace std::placeholders;

static std::atomic<bool> task_one_ran{false};

static Time our_time;

static void run_delayed()
{
    our_time.sleep_for(std::chrono::milliseconds(50));
    task_one_ran = true;
}

TEST(ThreadPool, SimpleTask)
{
    ThreadPool tp(3);
    ASSERT_TRUE(tp.start());

    task_one_ran = false;
    tp.enqueue(std::bind(run_delayed));

    our_time.sleep_for(std::chrono::milliseconds(25));
    EXPECT_FALSE(task_one_ran);
    our_time.sleep_for(std::chrono::milliseconds(50));
    EXPECT_TRUE(task_one_ran);
}

static void add_first_to_second(int arg1, int &arg2)
{
    arg2 += arg1;
}

TEST(ThreadPool, SimpleTaskWithArgs)
{
    ThreadPool tp(3);
    ASSERT_TRUE(tp.start());

    const int first = 5;
    int second = 10;
    const int sum = first + second;

    tp.enqueue(std::bind(add_first_to_second, first, std::ref(second)));

    our_time.sleep_for(std::chrono::milliseconds(25));
    EXPECT_EQ(second, sum);
}

TEST(ThreadPool, LambdaWithArgs)
{
    ThreadPool tp(3);
    ASSERT_TRUE(tp.start());

    const int first = 5;
    int second = 10;
    const int sum = first + second;

    tp.enqueue([first, &second]() { second += first; });

    our_time.sleep_for(std::chrono::milliseconds(25));
    EXPECT_EQ(second, sum);
}

TEST(ThreadPool, ManyTasks)
{
    ThreadPool tp(3);
    ASSERT_TRUE(tp.start());

    const int tasks_num = 100;
    int tasks[tasks_num]{};

    for (int i = 0; i < tasks_num; ++i) {
        tp.enqueue([&tasks, i]() { tasks[i] = i; });
    }

    our_time.sleep_for(std::chrono::milliseconds(25));

    for (int i = 0; i < tasks_num; ++i) {
        EXPECT_EQ(tasks[i], i);
    }
}
<commit_msg>core: fix macOS CI<commit_after>#include "thread_pool.h"
#include <gtest/gtest.h>
#include <atomic>

// We don't (yet) use fake time for this
// because the threads actually need to run.
//#ifdef FAKE_TIME
//#define Time FakeTime
//#endif

using namespace dronecode_sdk;
using namespace std::placeholders;

static std::atomic<bool> task_one_ran{false};

static Time our_time;

static void run_delayed()
{
    our_time.sleep_for(std::chrono::milliseconds(250));
    task_one_ran = true;
}

TEST(ThreadPool, SimpleTask)
{
    ThreadPool tp(3);
    ASSERT_TRUE(tp.start());

    task_one_ran = false;
    tp.enqueue(std::bind(run_delayed));

    our_time.sleep_for(std::chrono::milliseconds(125));
    EXPECT_FALSE(task_one_ran);
    our_time.sleep_for(std::chrono::milliseconds(250));
    EXPECT_TRUE(task_one_ran);
}

static void add_first_to_second(int arg1, int &arg2)
{
    arg2 += arg1;
}

TEST(ThreadPool, SimpleTaskWithArgs)
{
    ThreadPool tp(3);
    ASSERT_TRUE(tp.start());

    const int first = 5;
    int second = 10;
    const int sum = first + second;

    tp.enqueue(std::bind(add_first_to_second, first, std::ref(second)));

    our_time.sleep_for(std::chrono::milliseconds(125));
    EXPECT_EQ(second, sum);
}

TEST(ThreadPool, LambdaWithArgs)
{
    ThreadPool tp(3);
    ASSERT_TRUE(tp.start());

    const int first = 5;
    int second = 10;
    const int sum = first + second;

    tp.enqueue([first, &second]() { second += first; });

    our_time.sleep_for(std::chrono::milliseconds(125));
    EXPECT_EQ(second, sum);
}

TEST(ThreadPool, ManyTasks)
{
    ThreadPool tp(3);
    ASSERT_TRUE(tp.start());

    const int tasks_num = 100;
    int tasks[tasks_num]{};

    for (int i = 0; i < tasks_num; ++i) {
        tp.enqueue([&tasks, i]() { tasks[i] = i; });
    }

    our_time.sleep_for(std::chrono::milliseconds(125));

    for (int i = 0; i < tasks_num; ++i) {
        EXPECT_EQ(tasks[i], i);
    }
}
<|endoftext|>


<commit_before>#pragma once

#include "stoch_loop.hpp"
#include "math/tune_fixed.hpp"

namespace math
{
        ///
        /// \brief restart methods for Nesterov's accelerated gradient
        ///     see "Adaptive Restart for Accelerated Gradient Schemes",
        ///     by Brendan O’Donoghue & Emmanuel Candes, 2013
        ///
        enum class ag_restart
        {
                none,
                function,
                gradient
        };

        ///
        /// \brief stochastic Nesterov's accelerated gradient (descent)
        ///     see "Adaptive Restart for Accelerated Gradient Schemes",
        ///     by Brendan O’Donoghue & Emmanuel Candes, 2013
        ///
        template
        <
                typename tproblem,              ///< optimization problem
                ag_restart trestart             ///< restart method
        >
        struct stoch_ag_base_t
        {
                using param_t = stoch_params_t<tproblem>;
                using tstate = typename param_t::tstate;
                using tscalar = typename param_t::tscalar;
                using tvector = typename param_t::tvector;

                ///
                /// \brief minimize starting from the initial guess x0
                ///
                tstate operator()(const param_t& param, const tproblem& problem, const tvector& x0) const
                {
                        const auto alpha0s = { 1e-4, 1e-3, 1e-2, 1e-1, 1e+0 };
                        const auto qs = { 1e-6, 1e-4, 1e-2, 1e-1, 1e+0 };

                        const auto op = [&] (const auto alpha0, const auto q)
                        {
                                return this->operator()(param.tunable(), problem, x0, alpha0, q);
                        };

                        const auto config = math::tune_fixed(op, alpha0s, qs);
                        const auto opt_alpha0 = std::get<1>(config);
                        const auto opt_q = std::get<2>(config);

                        return operator()(param, problem, x0, opt_alpha0, opt_q);
                }

                ///
                /// \brief minimize starting from the initial guess x0
                ///
                tstate operator()(const param_t& param, const tproblem& problem, const tvector& x0,
                        const tscalar alpha0, const tscalar q) const
                {
                        assert(problem.size() == x0.size());

                        // initial state
                        tstate istate(problem, x0);

                        // current & previous iterations
                        tvector cx = istate.x;
                        tvector px = istate.x;
                        tvector cy = istate.x;
                        tvector py = istate.x;

                        tscalar cfx = istate.f;
                        tscalar pfx = istate.f;

                        tscalar ptheta = 1;
                        tscalar ctheta = 1;

                        const auto get_theta = [] (const auto ptheta, const auto q)
                        {
                                const auto a = tscalar(1);
                                const auto b = ptheta * ptheta - q;
                                const auto c = - ptheta * ptheta;

                                return (-b + std::sqrt(b * b - 4 * a * c)) / (2 * a);
                        };

                        const auto get_beta = [] (const auto ptheta, const auto ctheta)
                        {
                                return ptheta * (1 - ptheta) / (ptheta * ptheta + ctheta);
                        };

                        const auto op_iter = [&] (tstate& cstate, const std::size_t)
                        {
                                // learning rate
                                const tscalar alpha = alpha0;

                                // momentum
                                ctheta = get_theta(ptheta, q);
                                const tscalar beta = get_beta(ptheta, ctheta);

                                // update solution
                                cstate.update(problem, py);
                                cx = py - alpha * cstate.g;
                                cy = px + beta * (cx - px);

                                switch (trestart)
                                {
                                case ag_restart::none:
                                        break;

                                case ag_restart::function:
                                        if ((cfx = problem(cx)) > pfx)
                                        {
                                                ptheta = 1;
                                        }
                                        break;

                                case ag_restart::gradient:
                                        if (cstate.g.dot(cx - px) > tscalar(0))
                                        {
                                                ptheta = 1;
                                        }
                                        break;
                                }

                                // next iteration
                                px = cx;
                                py = cy;
                                pfx = cfx;
                                ptheta = ctheta;
                        };

                        const auto op_epoch = [&] (tstate& cstate)
                        {
                                cstate.update(problem, cx);
                        };

                        // OK, assembly the optimizer
                        return stoch_loop(param, istate, op_iter, op_epoch);
                }
        };

        // create various AG implementations
        template <typename tproblem>
        using stoch_ag_t = stoch_ag_base_t<tproblem, ag_restart::none>;

        template <typename tproblem>
        using stoch_agfr_t = stoch_ag_base_t<tproblem, ag_restart::function>;

        template <typename tproblem>
        using stoch_aggr_t = stoch_ag_base_t<tproblem, ag_restart::gradient>;
}

<commit_msg>fix AG optimizers<commit_after>#pragma once

#include "stoch_loop.hpp"
#include "math/tune_fixed.hpp"

namespace math
{
        ///
        /// \brief restart methods for Nesterov's accelerated gradient
        ///     see "Adaptive Restart for Accelerated Gradient Schemes",
        ///     by Brendan O’Donoghue & Emmanuel Candes, 2013
        ///
        enum class ag_restart
        {
                none,
                function,
                gradient
        };

        ///
        /// \brief stochastic Nesterov's accelerated gradient (descent)
        ///     see "Adaptive Restart for Accelerated Gradient Schemes",
        ///     by Brendan O’Donoghue & Emmanuel Candes, 2013
        ///
        template
        <
                typename tproblem,              ///< optimization problem
                ag_restart trestart             ///< restart method
        >
        struct stoch_ag_base_t
        {
                using param_t = stoch_params_t<tproblem>;
                using tstate = typename param_t::tstate;
                using tscalar = typename param_t::tscalar;
                using tvector = typename param_t::tvector;

                ///
                /// \brief minimize starting from the initial guess x0
                ///
                tstate operator()(const param_t& param, const tproblem& problem, const tvector& x0) const
                {
                        const auto alpha0s = { 1e-4, 1e-3, 1e-2, 1e-1, 1e+0 };
                        const auto qs = { 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e+0 };

                        const auto op = [&] (const auto alpha0, const auto q)
                        {
                                return this->operator()(param.tunable(), problem, x0, alpha0, q);
                        };

                        const auto config = math::tune_fixed(op, alpha0s, qs);
                        const auto opt_alpha0 = std::get<1>(config);
                        const auto opt_q = std::get<2>(config);

                        return operator()(param, problem, x0, opt_alpha0, opt_q);
                }

                ///
                /// \brief minimize starting from the initial guess x0
                ///
                tstate operator()(const param_t& param, const tproblem& problem, const tvector& x0,
                        const tscalar alpha0, const tscalar q) const
                {
                        assert(problem.size() == x0.size());

                        // initial state
                        tstate istate(problem, x0);

                        // current & previous iterations
                        tvector cx = istate.x;
                        tvector px = istate.x;
                        tvector cy = istate.x;
                        tvector py = istate.x;

                        tscalar cfx = istate.f;
                        tscalar pfx = istate.f;

                        tscalar ptheta = 1;
                        tscalar ctheta = 1;

                        const auto get_theta = [] (const auto ptheta, const auto q)
                        {
                                const auto a = tscalar(1);
                                const auto b = ptheta * ptheta - q;
                                const auto c = - ptheta * ptheta;

                                return (-b + std::sqrt(b * b - 4 * a * c)) / (2 * a);
                        };

                        const auto get_beta = [] (const auto ptheta, const auto ctheta)
                        {
                                return ptheta * (1 - ptheta) / (ptheta * ptheta + ctheta);
                        };

                        const auto op_iter = [&] (tstate& cstate, const std::size_t)
                        {
                                // learning rate
                                const tscalar alpha = alpha0;

                                // momentum
                                ctheta = get_theta(ptheta, q);
                                const tscalar beta = get_beta(ptheta, ctheta);

                                // update solution
                                cstate.update(problem, py);
                                cx = py - alpha * cstate.g;
                                cy = cx + beta * (cx - px);

                                switch (trestart)
                                {
                                case ag_restart::none:
                                        break;

                                case ag_restart::function:
                                        if ((cfx = problem(cx)) > pfx)
                                        {
                                                ctheta = 1;
                                        }
                                        break;

                                case ag_restart::gradient:
                                        if (cstate.g.dot(cx - px) > tscalar(0))
                                        {
                                                ctheta = 1;
                                        }
                                        break;
                                }

                                // next iteration
                                px = cx;
                                py = cy;
                                pfx = cfx;
                                ptheta = ctheta;
                        };

                        const auto op_epoch = [&] (tstate& cstate)
                        {
                                cstate.update(problem, cx);
                        };

                        // OK, assembly the optimizer
                        return stoch_loop(param, istate, op_iter, op_epoch);
                }
        };

        // create various AG implementations
        template <typename tproblem>
        using stoch_ag_t = stoch_ag_base_t<tproblem, ag_restart::none>;

        template <typename tproblem>
        using stoch_agfr_t = stoch_ag_base_t<tproblem, ag_restart::function>;

        template <typename tproblem>
        using stoch_aggr_t = stoch_ag_base_t<tproblem, ag_restart::gradient>;
}

<|endoftext|>


<commit_before>#pragma once

#include <cstdint>
#include <functional>
#include <iterator>
#include <unordered_map>
#include <vector>

#include <algorithms.hpp>
#include <backends/event.hpp>
#include <cell_group.hpp>
#include <cell.hpp>
#include <common_types.hpp>
#include <event_binner.hpp>
#include <event_queue.hpp>
#include <recipe.hpp>
#include <sampler_map.hpp>
#include <sampling.hpp>
#include <spike.hpp>
#include <util/debug.hpp>
#include <util/double_buffer.hpp>
#include <util/filter.hpp>
#include <util/partition.hpp>
#include <util/range.hpp>
#include <util/unique_any.hpp>

#include <profiling/profiler.hpp>

namespace arb {

template <typename LoweredCell>
class mc_cell_group: public cell_group {
public:
    using lowered_cell_type = LoweredCell;
    using value_type = typename lowered_cell_type::value_type;
    using size_type  = typename lowered_cell_type::value_type;

    mc_cell_group() = default;

    mc_cell_group(std::vector<cell_gid_type> gids, const recipe& rec):
        gids_(std::move(gids))
    {
        // Build lookup table for gid to local index.
        for (auto i: util::make_span(0, gids_.size())) {
            gid_index_map_[gids_[i]] = i;
        }

        // Create lookup structure for target ids.
        build_target_handle_partition(rec);
        std::size_t n_targets = target_handle_divisions_.back();

        // Pre-allocate space to store handles, probe map.
        auto n_probes = util::sum_by(gids_, [&rec](cell_gid_type i) { return rec.num_probes(i); });
        probe_map_.reserve(n_probes);
        target_handles_.reserve(n_targets);

        // Construct cell implementation, retrieving handles and maps.
        lowered_.initialize(gids_, rec, target_handles_, probe_map_);

        // Create a list of the global identifiers for the spike sources
        for (auto source_gid: gids_) {
            for (cell_lid_type lid = 0; lid<rec.num_sources(source_gid); ++lid) {
                spike_sources_.push_back({source_gid, lid});
            }
        }
        spike_sources_.shrink_to_fit();

        // Create event lane buffers.
        // There is one set for each epoch: current and next.
        event_lanes_.resize(2);
        // For each epoch there is one lane for each cell in the cell group.
        event_lanes_[0].resize(gids_.size());
        event_lanes_[1].resize(gids_.size());
    }

    cell_kind get_cell_kind() const override {
        return cell_kind::cable1d_neuron;
    }

    void reset() override {
        spikes_.clear();
        reset_samplers();
        for (auto& b: binners_) {
            b.reset();
        }
        lowered_.reset();
        for (auto& lanes: event_lanes_) {
            for (auto& lane: lanes) {
                lane.clear();
            }
        }
    }

    void set_binning_policy(binning_kind policy, time_type bin_interval) override {
        binners_.clear();
        binners_.resize(gids_.size(), event_binner(policy, bin_interval));
    }

    void advance(epoch ep, time_type dt) override {
        PE("advance");
        EXPECTS(lowered_.state_synchronized());
        time_type tstart = lowered_.min_time();

        PE("event-setup");
        const auto& lc = event_lanes(ep.id);
        staged_events_.clear();
        for (auto lid: util::make_span(0, gids_.size())) {
            auto& lane = lc[lid];
            for (auto e: lane) {
                if (e.time>=ep.tfinal) break;
                e.time = binners_[lid].bin(e.time, tstart);
                auto h = target_handles_[target_handle_divisions_[lid]+e.target.index];
                auto ev = deliverable_event(e.time, h, e.weight);
                staged_events_.push_back(ev);
            }
        }
        PL();

        // Create sample events and delivery information.
        //
        // For each (schedule, sampler, probe set) in the sampler association
        // map that will be triggered in this integration interval, create
        // sample events for the lowered cell, one for each scheduled sample
        // time and probe in the probe set.
        //
        // Each event is associated with an offset into the sample data and
        // time buffers; these are assigned contiguously such that one call to
        // a sampler callback can be represented by a `sampler_call_info`
        // value as defined below, grouping together all the samples of the
        // same probe for this callback in this association.

        struct sampler_call_info {
            sampler_function sampler;
            cell_member_type probe_id;
            probe_tag tag;

            // Offsets are into lowered cell sample time and event arrays.
            sample_size_type begin_offset;
            sample_size_type end_offset;
        };

        PE("sample-event-setup");
        std::vector<sampler_call_info> call_info;

        std::vector<sample_event> sample_events;
        sample_size_type n_samples = 0;
        sample_size_type max_samples_per_call = 0;

        for (auto& sa: sampler_map_) {
            auto sample_times = sa.sched.events(tstart, ep.tfinal);
            if (sample_times.empty()) {
                continue;
            }

            sample_size_type n_times = sample_times.size();
            max_samples_per_call = std::max(max_samples_per_call, n_times);

            for (cell_member_type pid: sa.probe_ids) {
                auto cell_index = gid_to_index(pid.gid);
                auto p = probe_map_[pid];

                call_info.push_back({sa.sampler, pid, p.tag, n_samples, n_samples+n_times});

                for (auto t: sample_times) {
                    sample_event ev{t, cell_index, {p.handle, n_samples++}};
                    sample_events.push_back(ev);
                }
            }
        }

        // Sample events must be ordered by time for the lowered cell.
        util::sort_by(sample_events, [](const sample_event& ev) { return event_time(ev); });
        PL();

        // Run integration.
        lowered_.setup_integration(ep.tfinal, dt, staged_events_, std::move(sample_events));
        PE("integrator-steps");

        while (!lowered_.integration_complete()) {
            lowered_.step_integration();
            if (util::is_debug_mode() && !lowered_.is_physical_solution()) {
                std::cerr << "warning: solution out of bounds  at (max) t "
                          << lowered_.max_time() << " ms\n";
            }
        }
        PL();

        // For each sampler callback registered in `call_info`, construct the
        // vector of sample entries from the lowered cell sample times and values
        // and then call the callback.

        PE("sample-deliver");
        std::vector<sample_record> sample_records;
        sample_records.reserve(max_samples_per_call);

        auto sample_time = lowered_.sample_time();
        auto sample_value = lowered_.sample_value();

        for (auto& sc: call_info) {
            sample_records.clear();
            for (auto i = sc.begin_offset; i!=sc.end_offset; ++i) {
                sample_records.push_back(sample_record{time_type(sample_time[i]), &sample_value[i]});
            }

            sc.sampler(sc.probe_id, sc.tag, sc.end_offset-sc.begin_offset, sample_records.data());
        }
        PL();

        // Copy out spike voltage threshold crossings from the back end, then
        // generate spikes with global spike source ids. The threshold crossings
        // record the local spike source index, which must be converted to a
        // global index for spike communication.

        PE("spike-retrieve");
        for (auto c: lowered_.get_spikes()) {
            spikes_.push_back({spike_sources_[c.index], time_type(c.time)});
        }

        // Now that the spikes have been generated, clear the old crossings
        // to get ready to record spikes from the next integration period.

        lowered_.clear_spikes();
        PL();

        PL();
    }

    void enqueue_events(
            epoch ep,
            util::subrange_view_type<std::vector<std::vector<postsynaptic_spike_event>>> events) override
    {
        using pse = postsynaptic_spike_event;

        // Make convenience variables for event lanes:
        //  lf: event lanes for the next epoch
        //  lc: event lanes for the current epoch
        auto& lf = event_lanes(ep.id+1);
        const auto& lc = event_lanes(ep.id);

        // For a cell, merge the incoming events with events in lc that are
        // not to be delivered in thie epoch, and store the result in lf.
        auto merge_future_events = [&](unsigned l) {
            PE("sort");
            // STEP 1: sort events in place in events[l]
            util::sort_by(events[l], [](const pse& e){return event_time(e);});
            PL();

            PE("merge");
            // STEP 2: clear lf to store merged list
            lf[l].clear();

            // STEP 3: merge new events and future events from lc into lf
            auto pos = std::lower_bound(lc[l].begin(), lc[l].end(), ep.tfinal, event_time_less());
            lf[l].resize(events[l].size()+std::distance(pos, lc[l].end()));
            std::merge(
                events[l].begin(), events[l].end(), pos, lc[l].end(), lf[l].begin(),
                [](const pse& l, const pse& r) {return l.time<r.time;});
            PL();
        };

        // This operation is independent for each cell, so it can be performed
        // in parallel if there are sufficient cells in the cell group.
        // TODO: use parallel loop based on argument to this function? This would
        //       allow the caller, which has more context, to decide whether a
        //       parallel loop is beneficial.
        if (gids_.size()>1) {
            threading::parallel_for::apply(0, gids_.size(), merge_future_events);
        }
        else {
            for (unsigned l=0; l<gids_.size(); ++l) {
                merge_future_events(l);
            }
        }
    }

    const std::vector<spike>& spikes() const override {
        return spikes_;
    }

    void clear_spikes() override {
        spikes_.clear();
    }

    const std::vector<cell_member_type>& spike_sources() const {
        return spike_sources_;
    }

    void add_sampler(sampler_association_handle h, cell_member_predicate probe_ids,
                     schedule sched, sampler_function fn, sampling_policy policy) override
    {
        std::vector<cell_member_type> probeset =
            util::assign_from(util::filter(util::keys(probe_map_), probe_ids));

        if (!probeset.empty()) {
            sampler_map_.add(h, sampler_association{std::move(sched), std::move(fn), std::move(probeset)});
        }
    }

    void remove_sampler(sampler_association_handle h) override {
        sampler_map_.remove(h);
    }

    void remove_all_samplers() override {
        sampler_map_.clear();
    }

private:
    std::vector<std::vector<postsynaptic_spike_event>>& event_lanes(std::size_t epoch_id) {
        return event_lanes_[epoch_id%2];
    }

    // List of the gids of the cells in the group.
    std::vector<cell_gid_type> gids_;

    // Hash table for converting gid to local index
    std::unordered_map<cell_gid_type, cell_gid_type> gid_index_map_;

    // The lowered cell state (e.g. FVM) of the cell.
    lowered_cell_type lowered_;

    // Spike detectors attached to the cell.
    std::vector<cell_member_type> spike_sources_;

    // Spikes that are generated.
    std::vector<spike> spikes_;

    // Event time binning manager.
    std::vector<event_binner> binners_;

    // Pending events to be delivered.
    std::vector<std::vector<std::vector<postsynaptic_spike_event>>> event_lanes_;
    std::vector<deliverable_event> staged_events_;

    // Pending samples to be taken.
    event_queue<sample_event> sample_events_;

    // Handles for accessing lowered cell.
    using target_handle = typename lowered_cell_type::target_handle;
    std::vector<target_handle> target_handles_;

    // Maps probe ids to probe handles (from lowered cell) and tags (from probe descriptions).
    using probe_handle = typename lowered_cell_type::probe_handle;
    probe_association_map<probe_handle> probe_map_;

    // Collection of samplers to be run against probes in this group.
    sampler_association_map sampler_map_;

    // Lookup table for target ids -> local target handle indices.
    std::vector<std::size_t> target_handle_divisions_;

    // Build handle index lookup tables.
    void build_target_handle_partition(const recipe& rec) {
        util::make_partition(target_handle_divisions_,
            util::transform_view(gids_, [&rec](cell_gid_type i) { return rec.num_targets(i); }));
    }

    // Get target handle from target id.
    target_handle get_target_handle(cell_member_type id) const {
        return target_handles_[target_handle_divisions_[gid_to_index(id.gid)]+id.index];
    }

    void reset_samplers() {
        // clear all pending sample events and reset to start at time 0
        sample_events_.clear();
        for (auto &assoc: sampler_map_) {
            assoc.sched.reset();
        }
    }

    cell_gid_type gid_to_index(cell_gid_type gid) const {
        auto it = gid_index_map_.find(gid);
        EXPECTS(it!=gid_index_map_.end());
        return it->second;
    }
};

} // namespace arb
<commit_msg>Set a default binning policy in mc_cell_group (#384)<commit_after>#pragma once

#include <cstdint>
#include <functional>
#include <iterator>
#include <unordered_map>
#include <vector>

#include <algorithms.hpp>
#include <backends/event.hpp>
#include <cell_group.hpp>
#include <cell.hpp>
#include <common_types.hpp>
#include <event_binner.hpp>
#include <event_queue.hpp>
#include <recipe.hpp>
#include <sampler_map.hpp>
#include <sampling.hpp>
#include <spike.hpp>
#include <util/debug.hpp>
#include <util/double_buffer.hpp>
#include <util/filter.hpp>
#include <util/partition.hpp>
#include <util/range.hpp>
#include <util/unique_any.hpp>

#include <profiling/profiler.hpp>

namespace arb {

template <typename LoweredCell>
class mc_cell_group: public cell_group {
public:
    using lowered_cell_type = LoweredCell;
    using value_type = typename lowered_cell_type::value_type;
    using size_type  = typename lowered_cell_type::value_type;

    mc_cell_group() = default;

    mc_cell_group(std::vector<cell_gid_type> gids, const recipe& rec):
        gids_(std::move(gids))
    {
        // Default to no binning of events
        set_binning_policy(binning_kind::none, 0);

        // Build lookup table for gid to local index.
        for (auto i: util::make_span(0, gids_.size())) {
            gid_index_map_[gids_[i]] = i;
        }

        // Create lookup structure for target ids.
        build_target_handle_partition(rec);
        std::size_t n_targets = target_handle_divisions_.back();

        // Pre-allocate space to store handles, probe map.
        auto n_probes = util::sum_by(gids_, [&rec](cell_gid_type i) { return rec.num_probes(i); });
        probe_map_.reserve(n_probes);
        target_handles_.reserve(n_targets);

        // Construct cell implementation, retrieving handles and maps.
        lowered_.initialize(gids_, rec, target_handles_, probe_map_);

        // Create a list of the global identifiers for the spike sources
        for (auto source_gid: gids_) {
            for (cell_lid_type lid = 0; lid<rec.num_sources(source_gid); ++lid) {
                spike_sources_.push_back({source_gid, lid});
            }
        }
        spike_sources_.shrink_to_fit();

        // Create event lane buffers.
        // There is one set for each epoch: current and next.
        event_lanes_.resize(2);
        // For each epoch there is one lane for each cell in the cell group.
        event_lanes_[0].resize(gids_.size());
        event_lanes_[1].resize(gids_.size());
    }

    cell_kind get_cell_kind() const override {
        return cell_kind::cable1d_neuron;
    }

    void reset() override {
        spikes_.clear();
        reset_samplers();
        for (auto& b: binners_) {
            b.reset();
        }
        lowered_.reset();
        for (auto& lanes: event_lanes_) {
            for (auto& lane: lanes) {
                lane.clear();
            }
        }
    }

    void set_binning_policy(binning_kind policy, time_type bin_interval) override {
        binners_.clear();
        binners_.resize(gids_.size(), event_binner(policy, bin_interval));
    }

    void advance(epoch ep, time_type dt) override {
        PE("advance");
        EXPECTS(lowered_.state_synchronized());
        time_type tstart = lowered_.min_time();

        PE("event-setup");
        const auto& lc = event_lanes(ep.id);
        staged_events_.clear();
        for (auto lid: util::make_span(0, gids_.size())) {
            auto& lane = lc[lid];
            for (auto e: lane) {
                if (e.time>=ep.tfinal) break;
                e.time = binners_[lid].bin(e.time, tstart);
                auto h = target_handles_[target_handle_divisions_[lid]+e.target.index];
                auto ev = deliverable_event(e.time, h, e.weight);
                staged_events_.push_back(ev);
            }
        }
        PL();

        // Create sample events and delivery information.
        //
        // For each (schedule, sampler, probe set) in the sampler association
        // map that will be triggered in this integration interval, create
        // sample events for the lowered cell, one for each scheduled sample
        // time and probe in the probe set.
        //
        // Each event is associated with an offset into the sample data and
        // time buffers; these are assigned contiguously such that one call to
        // a sampler callback can be represented by a `sampler_call_info`
        // value as defined below, grouping together all the samples of the
        // same probe for this callback in this association.

        struct sampler_call_info {
            sampler_function sampler;
            cell_member_type probe_id;
            probe_tag tag;

            // Offsets are into lowered cell sample time and event arrays.
            sample_size_type begin_offset;
            sample_size_type end_offset;
        };

        PE("sample-event-setup");
        std::vector<sampler_call_info> call_info;

        std::vector<sample_event> sample_events;
        sample_size_type n_samples = 0;
        sample_size_type max_samples_per_call = 0;

        for (auto& sa: sampler_map_) {
            auto sample_times = sa.sched.events(tstart, ep.tfinal);
            if (sample_times.empty()) {
                continue;
            }

            sample_size_type n_times = sample_times.size();
            max_samples_per_call = std::max(max_samples_per_call, n_times);

            for (cell_member_type pid: sa.probe_ids) {
                auto cell_index = gid_to_index(pid.gid);
                auto p = probe_map_[pid];

                call_info.push_back({sa.sampler, pid, p.tag, n_samples, n_samples+n_times});

                for (auto t: sample_times) {
                    sample_event ev{t, cell_index, {p.handle, n_samples++}};
                    sample_events.push_back(ev);
                }
            }
        }

        // Sample events must be ordered by time for the lowered cell.
        util::sort_by(sample_events, [](const sample_event& ev) { return event_time(ev); });
        PL();

        // Run integration.
        lowered_.setup_integration(ep.tfinal, dt, staged_events_, std::move(sample_events));
        PE("integrator-steps");

        while (!lowered_.integration_complete()) {
            lowered_.step_integration();
            if (util::is_debug_mode() && !lowered_.is_physical_solution()) {
                std::cerr << "warning: solution out of bounds  at (max) t "
                          << lowered_.max_time() << " ms\n";
            }
        }
        PL();

        // For each sampler callback registered in `call_info`, construct the
        // vector of sample entries from the lowered cell sample times and values
        // and then call the callback.

        PE("sample-deliver");
        std::vector<sample_record> sample_records;
        sample_records.reserve(max_samples_per_call);

        auto sample_time = lowered_.sample_time();
        auto sample_value = lowered_.sample_value();

        for (auto& sc: call_info) {
            sample_records.clear();
            for (auto i = sc.begin_offset; i!=sc.end_offset; ++i) {
                sample_records.push_back(sample_record{time_type(sample_time[i]), &sample_value[i]});
            }

            sc.sampler(sc.probe_id, sc.tag, sc.end_offset-sc.begin_offset, sample_records.data());
        }
        PL();

        // Copy out spike voltage threshold crossings from the back end, then
        // generate spikes with global spike source ids. The threshold crossings
        // record the local spike source index, which must be converted to a
        // global index for spike communication.

        PE("spike-retrieve");
        for (auto c: lowered_.get_spikes()) {
            spikes_.push_back({spike_sources_[c.index], time_type(c.time)});
        }

        // Now that the spikes have been generated, clear the old crossings
        // to get ready to record spikes from the next integration period.

        lowered_.clear_spikes();
        PL();

        PL();
    }

    void enqueue_events(
            epoch ep,
            util::subrange_view_type<std::vector<std::vector<postsynaptic_spike_event>>> events) override
    {
        using pse = postsynaptic_spike_event;

        // Make convenience variables for event lanes:
        //  lf: event lanes for the next epoch
        //  lc: event lanes for the current epoch
        auto& lf = event_lanes(ep.id+1);
        const auto& lc = event_lanes(ep.id);

        // For a cell, merge the incoming events with events in lc that are
        // not to be delivered in thie epoch, and store the result in lf.
        auto merge_future_events = [&](unsigned l) {
            PE("sort");
            // STEP 1: sort events in place in events[l]
            util::sort_by(events[l], [](const pse& e){return event_time(e);});
            PL();

            PE("merge");
            // STEP 2: clear lf to store merged list
            lf[l].clear();

            // STEP 3: merge new events and future events from lc into lf
            auto pos = std::lower_bound(lc[l].begin(), lc[l].end(), ep.tfinal, event_time_less());
            lf[l].resize(events[l].size()+std::distance(pos, lc[l].end()));
            std::merge(
                events[l].begin(), events[l].end(), pos, lc[l].end(), lf[l].begin(),
                [](const pse& l, const pse& r) {return l.time<r.time;});
            PL();
        };

        // This operation is independent for each cell, so it can be performed
        // in parallel if there are sufficient cells in the cell group.
        // TODO: use parallel loop based on argument to this function? This would
        //       allow the caller, which has more context, to decide whether a
        //       parallel loop is beneficial.
        if (gids_.size()>1) {
            threading::parallel_for::apply(0, gids_.size(), merge_future_events);
        }
        else {
            for (unsigned l=0; l<gids_.size(); ++l) {
                merge_future_events(l);
            }
        }
    }

    const std::vector<spike>& spikes() const override {
        return spikes_;
    }

    void clear_spikes() override {
        spikes_.clear();
    }

    const std::vector<cell_member_type>& spike_sources() const {
        return spike_sources_;
    }

    void add_sampler(sampler_association_handle h, cell_member_predicate probe_ids,
                     schedule sched, sampler_function fn, sampling_policy policy) override
    {
        std::vector<cell_member_type> probeset =
            util::assign_from(util::filter(util::keys(probe_map_), probe_ids));

        if (!probeset.empty()) {
            sampler_map_.add(h, sampler_association{std::move(sched), std::move(fn), std::move(probeset)});
        }
    }

    void remove_sampler(sampler_association_handle h) override {
        sampler_map_.remove(h);
    }

    void remove_all_samplers() override {
        sampler_map_.clear();
    }

private:
    std::vector<std::vector<postsynaptic_spike_event>>& event_lanes(std::size_t epoch_id) {
        return event_lanes_[epoch_id%2];
    }

    // List of the gids of the cells in the group.
    std::vector<cell_gid_type> gids_;

    // Hash table for converting gid to local index
    std::unordered_map<cell_gid_type, cell_gid_type> gid_index_map_;

    // The lowered cell state (e.g. FVM) of the cell.
    lowered_cell_type lowered_;

    // Spike detectors attached to the cell.
    std::vector<cell_member_type> spike_sources_;

    // Spikes that are generated.
    std::vector<spike> spikes_;

    // Event time binning manager.
    std::vector<event_binner> binners_;

    // Pending events to be delivered.
    std::vector<std::vector<std::vector<postsynaptic_spike_event>>> event_lanes_;
    std::vector<deliverable_event> staged_events_;

    // Pending samples to be taken.
    event_queue<sample_event> sample_events_;

    // Handles for accessing lowered cell.
    using target_handle = typename lowered_cell_type::target_handle;
    std::vector<target_handle> target_handles_;

    // Maps probe ids to probe handles (from lowered cell) and tags (from probe descriptions).
    using probe_handle = typename lowered_cell_type::probe_handle;
    probe_association_map<probe_handle> probe_map_;

    // Collection of samplers to be run against probes in this group.
    sampler_association_map sampler_map_;

    // Lookup table for target ids -> local target handle indices.
    std::vector<std::size_t> target_handle_divisions_;

    // Build handle index lookup tables.
    void build_target_handle_partition(const recipe& rec) {
        util::make_partition(target_handle_divisions_,
            util::transform_view(gids_, [&rec](cell_gid_type i) { return rec.num_targets(i); }));
    }

    // Get target handle from target id.
    target_handle get_target_handle(cell_member_type id) const {
        return target_handles_[target_handle_divisions_[gid_to_index(id.gid)]+id.index];
    }

    void reset_samplers() {
        // clear all pending sample events and reset to start at time 0
        sample_events_.clear();
        for (auto &assoc: sampler_map_) {
            assoc.sched.reset();
        }
    }

    cell_gid_type gid_to_index(cell_gid_type gid) const {
        auto it = gid_index_map_.find(gid);
        EXPECTS(it!=gid_index_map_.end());
        return it->second;
    }
};

} // namespace arb
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: astconstant.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: rt $ $Date: 2004-03-30 16:44:23 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _IDLC_ASTCONSTANT_HXX_
#include <idlc/astconstant.hxx>
#endif
#ifndef _IDLC_ASTSCOPE_HXX_
#include <idlc/astscope.hxx>
#endif

#include "registry/writer.hxx"

using namespace ::rtl;

AstConstant::AstConstant(const ExprType type,
                         const NodeType nodeType,
                         AstExpression* pExpr,
                         const ::rtl::OString& name,
                         AstScope* pScope)
    : AstDeclaration(nodeType, name, pScope)
    , m_pConstValue(pExpr)
    , m_constValueType(type)
{
}

AstConstant::AstConstant(const ExprType type,
                         AstExpression* pExpr,
                         const ::rtl::OString& name,
                         AstScope* pScope)
    : AstDeclaration(NT_const, name, pScope)
    , m_pConstValue(pExpr)
    , m_constValueType(type)
{
}

AstConstant::~AstConstant()
{

}

sal_Bool AstConstant::dumpBlob(typereg::Writer & rBlob, sal_uInt16 index)
{
    RTConstValue    aConst;
    sal_Unicode*    str = NULL;

    AstExprValue *exprVal = getConstValue()->getExprValue();
    switch (getConstValueType())
    {
        case ET_short:
            aConst.m_type = RT_TYPE_INT16;
            aConst.m_value.aShort = exprVal->u.sval;
            break;
        case ET_ushort:
            aConst.m_type = RT_TYPE_UINT16;
            aConst.m_value.aUShort = exprVal->u.usval;
            break;
        case ET_long:
            aConst.m_type = RT_TYPE_INT32;
            aConst.m_value.aLong = exprVal->u.lval;
            break;
        case ET_ulong:
            aConst.m_type = RT_TYPE_UINT32;
            aConst.m_value.aULong = exprVal->u.ulval;
            break;
        case ET_hyper:
            aConst.m_type = RT_TYPE_INT64;
            aConst.m_value.aHyper = exprVal->u.hval;
            break;
        case ET_uhyper:
            aConst.m_type = RT_TYPE_UINT64;
            aConst.m_value.aUHyper = exprVal->u.uhval;
            break;
        case ET_float:
            aConst.m_type = RT_TYPE_FLOAT;
            aConst.m_value.aFloat = exprVal->u.fval;
            break;
        case ET_double:
            aConst.m_type = RT_TYPE_DOUBLE;
            aConst.m_value.aDouble = exprVal->u.dval;
            break;
        case ET_char:
            break;
        case ET_byte:
            aConst.m_type = RT_TYPE_BYTE;
            aConst.m_value.aByte = exprVal->u.byval;
            break;
        case ET_boolean:
            aConst.m_type = RT_TYPE_BOOL;
            aConst.m_value.aBool = exprVal->u.bval;
            break;
        case ET_string:
            {
                aConst.m_type = RT_TYPE_STRING;
                ::rtl::OUString aTempStr( OUString::createFromAscii(exprVal->u.strval->getStr()));
                str = new sal_Unicode[aTempStr.getLength()+1];
                rtl_copyMemory(str, aTempStr.getStr(), (aTempStr.getLength()+1) * sizeof(sal_Unicode));
                aConst.m_value.aString = str;
            }
            break;
        default:
            {
                fprintf(stderr, "%s: exprtype to const type: cannot convert ExprType\n",
                        idlc()->getOptions()->getProgramName().getStr());
                return sal_False;
            }
    }

    OString name = getLocalName();

    OUString type;
    OUString fileName;
    if ( getNodeType() != NT_enum_val )
    {
        type = OStringToOUString(exprTypeToString(getConstValueType()), RTL_TEXTENCODING_UTF8);
        fileName = OStringToOUString(getFileName(), RTL_TEXTENCODING_UTF8);
    }

    rBlob.setFieldData(
        index, getDocumentation(), fileName, RT_ACCESS_CONST,
        OStringToOUString(name, RTL_TEXTENCODING_UTF8), type, aConst);
    if (str)
        delete[] str;

    return sal_True;
}
<commit_msg>INTEGRATION: CWS sb18 (1.4.4); FILE MERGED 2004/05/18 15:36:48 sb 1.4.4.1: #i21150# Added new feature: published.<commit_after>/*************************************************************************
 *
 *  $RCSfile: astconstant.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: obo $ $Date: 2004-06-03 15:06:56 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _IDLC_ASTCONSTANT_HXX_
#include <idlc/astconstant.hxx>
#endif
#ifndef _IDLC_ASTSCOPE_HXX_
#include <idlc/astscope.hxx>
#endif

#include "registry/writer.hxx"

using namespace ::rtl;

AstConstant::AstConstant(const ExprType type,
                         const NodeType nodeType,
                         AstExpression* pExpr,
                         const ::rtl::OString& name,
                         AstScope* pScope)
    : AstDeclaration(nodeType, name, pScope)
    , m_pConstValue(pExpr)
    , m_constValueType(type)
{
}

AstConstant::AstConstant(const ExprType type,
                         AstExpression* pExpr,
                         const ::rtl::OString& name,
                         AstScope* pScope)
    : AstDeclaration(NT_const, name, pScope)
    , m_pConstValue(pExpr)
    , m_constValueType(type)
{
}

AstConstant::~AstConstant()
{

}

sal_Bool AstConstant::dumpBlob(
    typereg::Writer & rBlob, sal_uInt16 index, bool published)
{
    RTConstValue    aConst;
    sal_Unicode*    str = NULL;

    AstExprValue *exprVal = getConstValue()->getExprValue();
    switch (getConstValueType())
    {
        case ET_short:
            aConst.m_type = RT_TYPE_INT16;
            aConst.m_value.aShort = exprVal->u.sval;
            break;
        case ET_ushort:
            aConst.m_type = RT_TYPE_UINT16;
            aConst.m_value.aUShort = exprVal->u.usval;
            break;
        case ET_long:
            aConst.m_type = RT_TYPE_INT32;
            aConst.m_value.aLong = exprVal->u.lval;
            break;
        case ET_ulong:
            aConst.m_type = RT_TYPE_UINT32;
            aConst.m_value.aULong = exprVal->u.ulval;
            break;
        case ET_hyper:
            aConst.m_type = RT_TYPE_INT64;
            aConst.m_value.aHyper = exprVal->u.hval;
            break;
        case ET_uhyper:
            aConst.m_type = RT_TYPE_UINT64;
            aConst.m_value.aUHyper = exprVal->u.uhval;
            break;
        case ET_float:
            aConst.m_type = RT_TYPE_FLOAT;
            aConst.m_value.aFloat = exprVal->u.fval;
            break;
        case ET_double:
            aConst.m_type = RT_TYPE_DOUBLE;
            aConst.m_value.aDouble = exprVal->u.dval;
            break;
        case ET_char:
            break;
        case ET_byte:
            aConst.m_type = RT_TYPE_BYTE;
            aConst.m_value.aByte = exprVal->u.byval;
            break;
        case ET_boolean:
            aConst.m_type = RT_TYPE_BOOL;
            aConst.m_value.aBool = exprVal->u.bval;
            break;
        case ET_string:
            {
                aConst.m_type = RT_TYPE_STRING;
                ::rtl::OUString aTempStr( OUString::createFromAscii(exprVal->u.strval->getStr()));
                str = new sal_Unicode[aTempStr.getLength()+1];
                rtl_copyMemory(str, aTempStr.getStr(), (aTempStr.getLength()+1) * sizeof(sal_Unicode));
                aConst.m_value.aString = str;
            }
            break;
        default:
            {
                fprintf(stderr, "%s: exprtype to const type: cannot convert ExprType\n",
                        idlc()->getOptions()->getProgramName().getStr());
                return sal_False;
            }
    }

    OString name = getLocalName();

    OUString type;
    OUString fileName;
    if ( getNodeType() != NT_enum_val )
    {
        type = OStringToOUString(exprTypeToString(getConstValueType()), RTL_TEXTENCODING_UTF8);
        fileName = OStringToOUString(getFileName(), RTL_TEXTENCODING_UTF8);
    }

    rBlob.setFieldData(
        index, getDocumentation(), fileName,
        RT_ACCESS_CONST | (published ? RT_ACCESS_PUBLISHED : 0),
        OStringToOUString(name, RTL_TEXTENCODING_UTF8), type, aConst);
    if (str)
        delete[] str;

    return sal_True;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: idlcproduce.cxx,v $
 *
 *  $Revision: 1.16 $
 *
 *  last change: $Author: ihi $ $Date: 2008-02-04 13:45:12 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_idlc.hxx"
#ifndef _IDLC_IDLC_HXX_
#include <idlc/idlc.hxx>
#endif
#ifndef _IDLC_ASTMODULE_HXX_
#include <idlc/astmodule.hxx>
#endif
#ifndef _RTL_STRBUF_HXX_
#include <rtl/strbuf.hxx>
#endif
#ifndef _OSL_FILE_HXX_
#include <osl/file.hxx>
#endif
#ifndef _OSL_THREAD_H_
#include <osl/thread.h>
#endif

#if defined(SAL_W32) || defined(SAL_OS2)
#include <io.h>
#include <direct.h>
#include <errno.h>
#endif

#ifdef SAL_UNX
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#endif

#include <string.h>

using namespace ::rtl;
using namespace ::osl;

StringList* pCreatedDirectories = NULL;

static sal_Bool checkOutputPath(const OString& completeName)
{
    OString sysPathName = convertToAbsoluteSystemPath(completeName);
    OStringBuffer buffer(sysPathName.getLength());

    if ( sysPathName.indexOf( SEPARATOR ) == -1 )
        return sal_True;

    sal_Int32 nIndex = 0;
    OString token(sysPathName.getToken(0, SEPARATOR, nIndex));
    const sal_Char* p = token.getStr();
    if (strcmp(p, "..") == 0
        || *(p+1) == ':'
        || strcmp(p, ".") == 0)
    {
        buffer.append(token);
        buffer.append(SEPARATOR);
    }
    else
        nIndex = 0;

    do
    {
        buffer.append(sysPathName.getToken(0, SEPARATOR, nIndex));

        if ( buffer.getLength() > 0 && nIndex != -1 )
        {
#if defined(SAL_UNX) || defined(SAL_OS2)
            if (mkdir((char*)buffer.getStr(), 0777) == -1)
#else
            if (mkdir((char*)buffer.getStr()) == -1)
#endif
            {
                if (errno == ENOENT)
                {
                    fprintf(stderr, "%s: cannot create directory '%s'\n",
                            idlc()->getOptions()->getProgramName().getStr(), buffer.getStr());
                    return sal_False;
                }
            } else
            {
                if ( !pCreatedDirectories )
                    pCreatedDirectories = new StringList();
                pCreatedDirectories->push_front(buffer.getStr());
            }
        }
        buffer.append(SEPARATOR);
    } while( nIndex != -1 );
    return sal_True;
}

static sal_Bool cleanPath()
{
    if ( pCreatedDirectories )
    {
        StringList::iterator iter = pCreatedDirectories->begin();
        StringList::iterator end = pCreatedDirectories->end();
        while ( iter != end )
        {
//#ifdef SAL_UNX
//          if (rmdir((char*)(*iter).getStr(), 0777) == -1)
//#else
            if (rmdir((char*)(*iter).getStr()) == -1)
//#endif
            {
                fprintf(stderr, "%s: cannot remove directory '%s'\n",
                        idlc()->getOptions()->getProgramName().getStr(), (*iter).getStr());
                return sal_False;
            }
            ++iter;
        }
        delete pCreatedDirectories;
    }
    return sal_True;
}

void removeIfExists(const OString& pathname)
{
    unlink(pathname.getStr());
}

sal_Int32 SAL_CALL produceFile(const OString& regFileName)
{
    Options* pOptions = idlc()->getOptions();

    OString regTmpName = regFileName.replaceAt(regFileName.getLength() -3, 3, "_idlc_");

    if ( !checkOutputPath(regFileName) )
    {
        fprintf(stderr, "%s: could not create path of registry file '%s'.\n",
                pOptions->getProgramName().getStr(), regFileName.getStr());
        return 1;
    }

    Registry regFile;

    removeIfExists(regTmpName);
    OString urlRegTmpName = convertToFileUrl(regTmpName);
    if ( regFile.create(OStringToOUString(urlRegTmpName, RTL_TEXTENCODING_UTF8)) )
    {
        fprintf(stderr, "%s: could not create registry file '%s'\n",
                pOptions->getProgramName().getStr(), regTmpName.getStr());
        removeIfExists(regTmpName);
        removeIfExists(regFileName);
        cleanPath();
        return 1;
    }

    RegistryKey rootKey;
    if ( regFile.openRootKey(rootKey) )
    {
        fprintf(stderr, "%s: could not open root of registry file '%s'\n",
                pOptions->getProgramName().getStr(), regFileName.getStr());
        removeIfExists(regTmpName);
        removeIfExists(regFileName);
        cleanPath();
        return 1;
    }

    // produce registry file
    if ( !idlc()->getRoot()->dump(rootKey) )
    {
        rootKey.closeKey();
        regFile.close();
        regFile.destroy(OStringToOUString(regFileName, RTL_TEXTENCODING_UTF8));
        removeIfExists(regFileName);
        cleanPath();
        return 1;
    }

    if ( rootKey.closeKey() )
    {
        fprintf(stderr, "%s: could not close root of registry file '%s'\n",
                pOptions->getProgramName().getStr(), regFileName.getStr());
        removeIfExists(regTmpName);
        removeIfExists(regFileName);
        cleanPath();
        return 1;
    }
    if ( regFile.close() )
    {
        fprintf(stderr, "%s: could not close registry file '%s'\n",
                pOptions->getProgramName().getStr(), regFileName.getStr());
        removeIfExists(regTmpName);
        removeIfExists(regFileName);
        cleanPath();
        return 1;
    }

    removeIfExists(regFileName);

    if ( File::move(OStringToOUString(regTmpName, osl_getThreadTextEncoding()),
                    OStringToOUString(regFileName, osl_getThreadTextEncoding())) != FileBase::E_None ) {
        fprintf(stderr, "%s: cannot rename temporary registry '%s' to '%s'\n",
                idlc()->getOptions()->getProgramName().getStr(),
                regTmpName.getStr(), regFileName.getStr());
        removeIfExists(regTmpName);
        cleanPath();
        return 1;
    }
    removeIfExists(regTmpName);

    return 0;
}
<commit_msg>INTEGRATION: CWS changefileheader (1.16.8); FILE MERGED 2008/04/01 15:20:34 thb 1.16.8.3: #i85898# Stripping all external header guards 2008/04/01 12:31:29 thb 1.16.8.2: #i85898# Stripping all external header guards 2008/03/31 07:23:51 rt 1.16.8.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: idlcproduce.cxx,v $
 * $Revision: 1.17 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_idlc.hxx"
#include <idlc/idlc.hxx>
#include <idlc/astmodule.hxx>
#include <rtl/strbuf.hxx>
#include <osl/file.hxx>
#include <osl/thread.h>

#if defined(SAL_W32) || defined(SAL_OS2)
#include <io.h>
#include <direct.h>
#include <errno.h>
#endif

#ifdef SAL_UNX
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#endif

#include <string.h>

using namespace ::rtl;
using namespace ::osl;

StringList* pCreatedDirectories = NULL;

static sal_Bool checkOutputPath(const OString& completeName)
{
    OString sysPathName = convertToAbsoluteSystemPath(completeName);
    OStringBuffer buffer(sysPathName.getLength());

    if ( sysPathName.indexOf( SEPARATOR ) == -1 )
        return sal_True;

    sal_Int32 nIndex = 0;
    OString token(sysPathName.getToken(0, SEPARATOR, nIndex));
    const sal_Char* p = token.getStr();
    if (strcmp(p, "..") == 0
        || *(p+1) == ':'
        || strcmp(p, ".") == 0)
    {
        buffer.append(token);
        buffer.append(SEPARATOR);
    }
    else
        nIndex = 0;

    do
    {
        buffer.append(sysPathName.getToken(0, SEPARATOR, nIndex));

        if ( buffer.getLength() > 0 && nIndex != -1 )
        {
#if defined(SAL_UNX) || defined(SAL_OS2)
            if (mkdir((char*)buffer.getStr(), 0777) == -1)
#else
            if (mkdir((char*)buffer.getStr()) == -1)
#endif
            {
                if (errno == ENOENT)
                {
                    fprintf(stderr, "%s: cannot create directory '%s'\n",
                            idlc()->getOptions()->getProgramName().getStr(), buffer.getStr());
                    return sal_False;
                }
            } else
            {
                if ( !pCreatedDirectories )
                    pCreatedDirectories = new StringList();
                pCreatedDirectories->push_front(buffer.getStr());
            }
        }
        buffer.append(SEPARATOR);
    } while( nIndex != -1 );
    return sal_True;
}

static sal_Bool cleanPath()
{
    if ( pCreatedDirectories )
    {
        StringList::iterator iter = pCreatedDirectories->begin();
        StringList::iterator end = pCreatedDirectories->end();
        while ( iter != end )
        {
//#ifdef SAL_UNX
//          if (rmdir((char*)(*iter).getStr(), 0777) == -1)
//#else
            if (rmdir((char*)(*iter).getStr()) == -1)
//#endif
            {
                fprintf(stderr, "%s: cannot remove directory '%s'\n",
                        idlc()->getOptions()->getProgramName().getStr(), (*iter).getStr());
                return sal_False;
            }
            ++iter;
        }
        delete pCreatedDirectories;
    }
    return sal_True;
}

void removeIfExists(const OString& pathname)
{
    unlink(pathname.getStr());
}

sal_Int32 SAL_CALL produceFile(const OString& regFileName)
{
    Options* pOptions = idlc()->getOptions();

    OString regTmpName = regFileName.replaceAt(regFileName.getLength() -3, 3, "_idlc_");

    if ( !checkOutputPath(regFileName) )
    {
        fprintf(stderr, "%s: could not create path of registry file '%s'.\n",
                pOptions->getProgramName().getStr(), regFileName.getStr());
        return 1;
    }

    Registry regFile;

    removeIfExists(regTmpName);
    OString urlRegTmpName = convertToFileUrl(regTmpName);
    if ( regFile.create(OStringToOUString(urlRegTmpName, RTL_TEXTENCODING_UTF8)) )
    {
        fprintf(stderr, "%s: could not create registry file '%s'\n",
                pOptions->getProgramName().getStr(), regTmpName.getStr());
        removeIfExists(regTmpName);
        removeIfExists(regFileName);
        cleanPath();
        return 1;
    }

    RegistryKey rootKey;
    if ( regFile.openRootKey(rootKey) )
    {
        fprintf(stderr, "%s: could not open root of registry file '%s'\n",
                pOptions->getProgramName().getStr(), regFileName.getStr());
        removeIfExists(regTmpName);
        removeIfExists(regFileName);
        cleanPath();
        return 1;
    }

    // produce registry file
    if ( !idlc()->getRoot()->dump(rootKey) )
    {
        rootKey.closeKey();
        regFile.close();
        regFile.destroy(OStringToOUString(regFileName, RTL_TEXTENCODING_UTF8));
        removeIfExists(regFileName);
        cleanPath();
        return 1;
    }

    if ( rootKey.closeKey() )
    {
        fprintf(stderr, "%s: could not close root of registry file '%s'\n",
                pOptions->getProgramName().getStr(), regFileName.getStr());
        removeIfExists(regTmpName);
        removeIfExists(regFileName);
        cleanPath();
        return 1;
    }
    if ( regFile.close() )
    {
        fprintf(stderr, "%s: could not close registry file '%s'\n",
                pOptions->getProgramName().getStr(), regFileName.getStr());
        removeIfExists(regTmpName);
        removeIfExists(regFileName);
        cleanPath();
        return 1;
    }

    removeIfExists(regFileName);

    if ( File::move(OStringToOUString(regTmpName, osl_getThreadTextEncoding()),
                    OStringToOUString(regFileName, osl_getThreadTextEncoding())) != FileBase::E_None ) {
        fprintf(stderr, "%s: cannot rename temporary registry '%s' to '%s'\n",
                idlc()->getOptions()->getProgramName().getStr(),
                regTmpName.getStr(), regFileName.getStr());
        removeIfExists(regTmpName);
        cleanPath();
        return 1;
    }
    removeIfExists(regTmpName);

    return 0;
}
<|endoftext|>


<commit_before>//
// Created by kanairen on 2016/06/14.
//

#include "TestLayer.h"
#include "TestSoftMaxLayer.h"
#include "TestGridLayer.h"
#include "TestConvLayer.h"
#include "TestMaxPoolLayer.h"

#include "TestModel.h"

int main() {

    // test Softmax Layer
    sm_layer::test_init();
    sm_layer::test_forward();
    sm_layer::test_backward();

    // test Grid Layer
    grid_layer::test_init();
    grid_layer::test_filter_outsize();

    // test Convolution Layer
    conv_layer::test_init();
    conv_layer::test_backward();

    // test MaxPooling Layer
    max_pool_layer::test_init();
    max_pool_layer::test_forward();
    max_pool_layer::test_backward();

    // test Model
    model::test_init();
    model::test_forward();
    model::test_backward();

}
<commit_msg>fix GoogleTestの呼び出しを追加<commit_after>//
// Created by kanairen on 2016/06/14.
//

#include "TestLayer.h"
#include "TestSoftMaxLayer.h"
#include "TestGridLayer.h"
#include "TestConvLayer.h"
#include "TestMaxPoolLayer.h"

#include "TestModel.h"

int main() {

    // test Softmax Layer
    sm_layer::test_init();
    sm_layer::test_forward();
    sm_layer::test_backward();

    // test Grid Layer
    grid_layer::test_init();
    grid_layer::test_filter_outsize();

    // test Convolution Layer
    conv_layer::test_init();
    conv_layer::test_backward();

    // test MaxPooling Layer
    max_pool_layer::test_init();
    max_pool_layer::test_forward();
    max_pool_layer::test_backward();

    // test Model
    model::test_init();
    model::test_forward();
    model::test_backward();

    return RUN_ALL_TESTS();

}
<|endoftext|>


<commit_before>#include "callback_object.hpp"
#include "callback_parameter.hpp"
#include "code/ylikuutio/common/any_value.hpp"
#include "code/ylikuutio/common/globals.hpp"
#include "code/ylikuutio/hierarchy/hierarchy_templates.hpp"

// Include standard headers
#include <cstddef>  // std::size_t
#include <iostream> // std::cout, std::cin, std::cerr
#include <memory>   // std::make_shared, std::shared_ptr
#include <string>   // std::string
#include <unordered_map> // std::unordered_map
#include <vector>   // std::vector

namespace yli
{
    namespace callback_system
    {
        void CallbackObject::bind_callback_parameter(yli::callback_system::CallbackParameter* const callback_parameter)
        {
            // get `childID` from `CallbackObject` and set pointer to `callback_parameter`.
            yli::hierarchy::bind_child_to_parent<yli::callback_system::CallbackParameter*>(
                    callback_parameter,
                    this->callback_parameter_pointer_vector,
                    this->free_callback_parameterID_queue,
                    this->number_of_callback_parameters);

            if (!callback_parameter->name.empty())
            {
                // This parameter is a named variable, so store it in `anyvalue_hashmap`.
                this->set_any_value(callback_parameter->name, std::make_shared<yli::datatypes::AnyValue>(*callback_parameter->any_value));
            }
        }

        void CallbackObject::unbind_callback_parameter(const std::size_t childID)
        {
            yli::hierarchy::unbind_child_from_parent(
                    childID,
                    this->callback_parameter_pointer_vector,
                    this->free_callback_parameterID_queue,
                    this->number_of_callback_parameters);
        }

        // getter function for callbacks and callback objects.
        std::shared_ptr<yli::datatypes::AnyValue> CallbackObject::get_any_value(const std::string& name) const
        {
            if (this->anyvalue_hashmap.count(name) != 1)
            {
                return nullptr;
            }

            return std::make_shared<yli::datatypes::AnyValue>(this->anyvalue_hashmap.at(name));
        }

        std::shared_ptr<yli::datatypes::AnyValue> CallbackObject::get_arg(const std::size_t arg_i) const
        {
            if (arg_i >= this->callback_parameter_pointer_vector.size())
            {
                std::cerr << "error #1 in CallbackObject code.\n";
                return nullptr;
            }

            yli::callback_system::CallbackParameter* callback_parameter = this->callback_parameter_pointer_vector.at(arg_i);

            if (callback_parameter == nullptr)
            {
                std::cerr << "error #2 in CallbackObject code.\n";
                return nullptr;
            }

            return std::make_shared<yli::datatypes::AnyValue>(*callback_parameter->any_value);
        }

        // setter function for callbacks and callback objects.
        void CallbackObject::set_any_value(const std::string& name, std::shared_ptr<yli::datatypes::AnyValue> any_value)
        {
            this->anyvalue_hashmap[name] = *any_value;
        }

        void CallbackObject::bind_to_parent()
        {
            this->parent->bind_callback_object(this);
        }

        void CallbackObject::set_new_callback(const InputParametersToAnyValueCallback callback)
        {
            this->callback = callback;
        }

        CallbackObject::CallbackObject(yli::callback_system::CallbackEngine* parent)
        {
            // constructor.
            this->childID = -1;
            this->callback = nullptr;
            this->parent = parent;

            this->number_of_callback_parameters = 0;

            // get childID from the CallbackEngine and set pointer to this CallbackObject.
            this->bind_to_parent();
        }

        CallbackObject::CallbackObject(const InputParametersToAnyValueCallback callback, yli::callback_system::CallbackEngine* parent)
        {
            // constructor.
            this->childID = -1;
            this->callback = callback;
            this->parent = parent;

            this->number_of_callback_parameters = 0;

            // get childID from the CallbackEngine and set pointer to this CallbackObject.
            this->bind_to_parent();
        }

        CallbackObject::~CallbackObject()
        {
            std::cout << "Callback object with childID " << this->childID << " will be destroyed.\n";

            // destroy all callback parameters of this callback object.
            std::cout << "All callback parameters of this callback object will be destroyed.\n";
            for (std::size_t child_i = 0; child_i < this->callback_parameter_pointer_vector.size(); child_i++)
            {
                delete this->callback_parameter_pointer_vector[child_i];
            }
        }

        void CallbackObject::set_callback_parameter_pointer(const std::size_t childID, yli::callback_system::CallbackParameter* const child_pointer)
        {
            this->callback_parameter_pointer_vector[childID] = child_pointer;

            if (child_pointer == nullptr)
            {
                if (childID == this->callback_parameter_pointer_vector.size() - 1)
                {
                    // OK, this is the biggest childID of all childID's of this 'object'.
                    // We can reduce the size of the child pointer vector at least by 1.
                    while ((!this->callback_parameter_pointer_vector.empty()) && (this->callback_parameter_pointer_vector.back() == nullptr))
                    {
                        // Reduce the size of child pointer vector by 1.
                        this->callback_parameter_pointer_vector.pop_back();
                    }
                }
            }
        }

        std::shared_ptr<yli::datatypes::AnyValue> CallbackObject::execute()
        {
            if (this->callback != nullptr)
            {
                return this->callback(this->parent, this, this->callback_parameter_pointer_vector);
            }
            return nullptr;
        }
    }
}
<commit_msg>`CallbackObject::bind_to_parent`: check `this->parent` for `nullptr`.<commit_after>#include "callback_object.hpp"
#include "callback_parameter.hpp"
#include "code/ylikuutio/common/any_value.hpp"
#include "code/ylikuutio/common/globals.hpp"
#include "code/ylikuutio/hierarchy/hierarchy_templates.hpp"

// Include standard headers
#include <cstddef>  // std::size_t
#include <iostream> // std::cout, std::cin, std::cerr
#include <memory>   // std::make_shared, std::shared_ptr
#include <string>   // std::string
#include <unordered_map> // std::unordered_map
#include <vector>   // std::vector

namespace yli
{
    namespace callback_system
    {
        void CallbackObject::bind_callback_parameter(yli::callback_system::CallbackParameter* const callback_parameter)
        {
            // get `childID` from `CallbackObject` and set pointer to `callback_parameter`.
            yli::hierarchy::bind_child_to_parent<yli::callback_system::CallbackParameter*>(
                    callback_parameter,
                    this->callback_parameter_pointer_vector,
                    this->free_callback_parameterID_queue,
                    this->number_of_callback_parameters);

            if (!callback_parameter->name.empty())
            {
                // This parameter is a named variable, so store it in `anyvalue_hashmap`.
                this->set_any_value(callback_parameter->name, std::make_shared<yli::datatypes::AnyValue>(*callback_parameter->any_value));
            }
        }

        void CallbackObject::unbind_callback_parameter(const std::size_t childID)
        {
            yli::hierarchy::unbind_child_from_parent(
                    childID,
                    this->callback_parameter_pointer_vector,
                    this->free_callback_parameterID_queue,
                    this->number_of_callback_parameters);
        }

        // getter function for callbacks and callback objects.
        std::shared_ptr<yli::datatypes::AnyValue> CallbackObject::get_any_value(const std::string& name) const
        {
            if (this->anyvalue_hashmap.count(name) != 1)
            {
                return nullptr;
            }

            return std::make_shared<yli::datatypes::AnyValue>(this->anyvalue_hashmap.at(name));
        }

        std::shared_ptr<yli::datatypes::AnyValue> CallbackObject::get_arg(const std::size_t arg_i) const
        {
            if (arg_i >= this->callback_parameter_pointer_vector.size())
            {
                std::cerr << "error #1 in CallbackObject code.\n";
                return nullptr;
            }

            yli::callback_system::CallbackParameter* callback_parameter = this->callback_parameter_pointer_vector.at(arg_i);

            if (callback_parameter == nullptr)
            {
                std::cerr << "error #2 in CallbackObject code.\n";
                return nullptr;
            }

            return std::make_shared<yli::datatypes::AnyValue>(*callback_parameter->any_value);
        }

        // setter function for callbacks and callback objects.
        void CallbackObject::set_any_value(const std::string& name, std::shared_ptr<yli::datatypes::AnyValue> any_value)
        {
            this->anyvalue_hashmap[name] = *any_value;
        }

        void CallbackObject::bind_to_parent()
        {
            // requirements:
            // `this->parent` must not be `nullptr`.

            yli::callback_system::CallbackEngine* const callback_engine = this->parent;

            if (callback_engine == nullptr)
            {
                std::cerr << "ERROR: `CallbackObject::bind_to_parent`: `callback_engine` is `nullptr`!\n";
                return;
            }

            callback_engine->bind_callback_object(this);
        }

        void CallbackObject::set_new_callback(const InputParametersToAnyValueCallback callback)
        {
            this->callback = callback;
        }

        CallbackObject::CallbackObject(yli::callback_system::CallbackEngine* parent)
        {
            // constructor.
            this->childID = -1;
            this->callback = nullptr;
            this->parent = parent;

            this->number_of_callback_parameters = 0;

            // get childID from the CallbackEngine and set pointer to this CallbackObject.
            this->bind_to_parent();
        }

        CallbackObject::CallbackObject(const InputParametersToAnyValueCallback callback, yli::callback_system::CallbackEngine* parent)
        {
            // constructor.
            this->childID = -1;
            this->callback = callback;
            this->parent = parent;

            this->number_of_callback_parameters = 0;

            // get childID from the CallbackEngine and set pointer to this CallbackObject.
            this->bind_to_parent();
        }

        CallbackObject::~CallbackObject()
        {
            std::cout << "Callback object with childID " << this->childID << " will be destroyed.\n";

            // destroy all callback parameters of this callback object.
            std::cout << "All callback parameters of this callback object will be destroyed.\n";
            for (std::size_t child_i = 0; child_i < this->callback_parameter_pointer_vector.size(); child_i++)
            {
                delete this->callback_parameter_pointer_vector[child_i];
            }
        }

        void CallbackObject::set_callback_parameter_pointer(const std::size_t childID, yli::callback_system::CallbackParameter* const child_pointer)
        {
            this->callback_parameter_pointer_vector[childID] = child_pointer;

            if (child_pointer == nullptr)
            {
                if (childID == this->callback_parameter_pointer_vector.size() - 1)
                {
                    // OK, this is the biggest childID of all childID's of this 'object'.
                    // We can reduce the size of the child pointer vector at least by 1.
                    while ((!this->callback_parameter_pointer_vector.empty()) && (this->callback_parameter_pointer_vector.back() == nullptr))
                    {
                        // Reduce the size of child pointer vector by 1.
                        this->callback_parameter_pointer_vector.pop_back();
                    }
                }
            }
        }

        std::shared_ptr<yli::datatypes::AnyValue> CallbackObject::execute()
        {
            if (this->callback != nullptr)
            {
                return this->callback(this->parent, this, this->callback_parameter_pointer_vector);
            }
            return nullptr;
        }
    }
}
<|endoftext|>