Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronised variable transfer #189

Merged
merged 5 commits into from
Mar 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 18 additions & 13 deletions src/cpp/algorithm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "cse/exception.hpp"

#include <algorithm>
#include <numeric>
#include <sstream>
#include <unordered_map>
#include <vector>
Expand Down Expand Up @@ -216,19 +217,23 @@ class fixed_step_algorithm::impl
void transfer_variables(const std::vector<connection>& connections)
{
for (const auto& c : connections) {
switch (c.input.type) {
case variable_type::real:
transfer_real(c.output, c.input);
break;
case variable_type::integer:
transfer_integer(c.output, c.input);
break;
case variable_type::boolean:
transfer_boolean(c.output, c.input);
break;
case variable_type::string:
transfer_string(c.output, c.input);
break;
const auto odf = simulators_[c.output.simulator].decimationFactor;
const auto idf = simulators_[c.input.simulator].decimationFactor;
if (stepCounter_ % std::lcm(odf, idf) == 0) {
switch (c.input.type) {
case variable_type::real:
transfer_real(c.output, c.input);
break;
case variable_type::integer:
transfer_integer(c.output, c.input);
break;
case variable_type::boolean:
transfer_boolean(c.output, c.input);
break;
case variable_type::string:
transfer_string(c.output, c.input);
break;
}
}
}
}
Expand Down
176 changes: 139 additions & 37 deletions src/cpp/slave_simulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ struct var_view_type
{
using type = T;
};

template<>
struct var_view_type<std::string>
{
using type = std::string_view;
};


template<typename T>
struct exposed_vars
struct get_variable_cache
{
std::vector<variable_index> indexes;
boost::container::vector<T> originalValues;
Expand Down Expand Up @@ -59,36 +61,132 @@ struct exposed_vars
}
}

void set(variable_index i, typename var_view_type<T>::type v)
void set_manipulator(variable_index i, std::function<T(T)> m)
{
const auto it = indexMapping.find(i);
if (it != indexMapping.end()) {
originalValues[it->second] = v;
} else {
manipulators[indexMapping[i]] = m;
}

void run_manipulators()
{
for (std::size_t i = 0; i < originalValues.size(); ++i) {
if (manipulators[i]) {
manipulatedValues[i] = manipulators[i](originalValues[i]);
} else {
manipulatedValues[i] = originalValues[i];
}
}
}
};


template<typename T>
class set_variable_cache
{
public:
void expose(variable_index i)
{
// TODO: Here, exposed_variable::lastValue will be initialised with
// the value T(). We ought to use the start value from the model
// description instead.
exposedVariables_.emplace(i, exposed_variable());
}

void set_value(variable_index i, typename var_view_type<T>::type v)
{
assert(!hasRunManipulators_);
const auto it = exposedVariables_.find(i);
if (it == exposedVariables_.end()) {
std::ostringstream oss;
oss << "variable_index " << i
<< " not found in exposed variables. Variables must be exposed before calling set()";
throw std::out_of_range(oss.str());
}
it->second.lastValue = v;
if (it->second.arrayIndex < 0) {
it->second.arrayIndex = indexes_.size();
assert(indexes_.size() == values_.size());
indexes_.emplace_back(i);
values_.emplace_back(v);
} else {
assert(indexes_[it->second.arrayIndex] == i);
values_[it->second.arrayIndex] = v;
}
}

void set_manipulator(variable_index i, std::function<T(T)> m)
{
manipulators[indexMapping[i]] = m;
assert(!hasRunManipulators_);
const auto it = exposedVariables_.find(i);
if (it == exposedVariables_.end()) {
std::ostringstream oss;
oss << "variable_index " << i
<< " not found in exposed variables. Variables must be exposed before calling set_manipulator()";
throw std::out_of_range(oss.str());
}
if (it->second.arrayIndex < 0) {
// Ensure that the simulator receives an updated value.
it->second.arrayIndex = indexes_.size();
assert(indexes_.size() == values_.size());
indexes_.emplace_back(i);
values_.emplace_back();
}
if (m) {
manipulators_[i] = m;
} else {
manipulators_.erase(i);
}
}

void run_manipulators()
std::pair<gsl::span<variable_index>, gsl::span<const T>> manipulate_and_get()
{
for (std::size_t i = 0; i < originalValues.size(); ++i) {
if (manipulators[i]) {
manipulatedValues[i] = manipulators[i](originalValues[i]);
} else {
manipulatedValues[i] = originalValues[i];
if (!hasRunManipulators_) {
assert(indexes_.size() == values_.size());
for (std::size_t i = 0; i < indexes_.size(); ++i) {
const auto iterator = manipulators_.find(indexes_[i]);
if (iterator != manipulators_.end()) {
values_[i] = iterator->second(values_[i]);
}
}
hasRunManipulators_ = true;
}
return std::pair(gsl::make_span(indexes_), gsl::make_span(values_));
}

void reset()
{
for (auto index : indexes_) {
exposedVariables_.at(index).arrayIndex = -1;
}
indexes_.clear();
values_.clear();
hasRunManipulators_ = false;
}

private:
struct exposed_variable
{
// The last set value of the variable.
T lastValue = T();

// The variable's index in the `indexes_` and `values_` arrays, or
// -1 if it hasn't been added to them yet.
std::ptrdiff_t arrayIndex = -1;
};

std::unordered_map<variable_index, exposed_variable> exposedVariables_;

// The manipulators associated with certain variables, and a flag that
// specifies whether they have been run on the values currently in
// `values_`.
std::unordered_map<variable_index, std::function<T(T)>> manipulators_;
bool hasRunManipulators_ = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is hasRunManipulators_ mainly a guard against setting values and manipulators during slave_simulator::set_variables()?

Copy link
Member Author

@kyllingstad kyllingstad Mar 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partly, but that's not its primary purpose. It is used in set_variable_cache::manipulate_and_get() to check whether the manipulators have already been run, to avoid running them multiple times on the same values if the function is called again. (That never happens now, but could easily happen in the future.)

Compare with how it was done before, and still is in get_variable_cache, where you first call run_manipulators() and then access the manipulatedValues vector directly. In the new class, I've made the member variables private and tried to offer a less error prone interface.


// The indexes and values of the variables that will be set next.
std::vector<variable_index> indexes_;
boost::container::vector<T> values_;
};


// Copies the contents of a contiguous-storage container or span into another.
template<typename Src, typename Tgt>
void copy_contents(Src&& src, Tgt&& tgt)
Expand Down Expand Up @@ -190,22 +288,22 @@ class slave_simulator::impl

void set_real(variable_index index, double value)
{
realSetCache_.set(index, value);
realSetCache_.set_value(index, value);
}

void set_integer(variable_index index, int value)
{
integerSetCache_.set(index, value);
integerSetCache_.set_value(index, value);
}

void set_boolean(variable_index index, bool value)
{
booleanSetCache_.set(index, value);
booleanSetCache_.set_value(index, value);
}

void set_string(variable_index index, std::string_view value)
{
stringSetCache_.set(index, value);
stringSetCache_.set_value(index, value);
}

void set_real_input_manipulator(
Expand Down Expand Up @@ -302,20 +400,24 @@ class slave_simulator::impl
private:
void set_variables()
{
realSetCache_.run_manipulators();
integerSetCache_.run_manipulators();
booleanSetCache_.run_manipulators();
stringSetCache_.run_manipulators();
const auto [realIndexes, realValues] = realSetCache_.manipulate_and_get();
const auto [integerIndexes, integerValues] = integerSetCache_.manipulate_and_get();
const auto [booleanIndexes, booleanValues] = booleanSetCache_.manipulate_and_get();
const auto [stringIndexes, stringValues] = stringSetCache_.manipulate_and_get();
slave_->set_variables(
gsl::make_span(realSetCache_.indexes),
gsl::make_span(realSetCache_.manipulatedValues),
gsl::make_span(integerSetCache_.indexes),
gsl::make_span(integerSetCache_.manipulatedValues),
gsl::make_span(booleanSetCache_.indexes),
gsl::make_span(booleanSetCache_.manipulatedValues),
gsl::make_span(stringSetCache_.indexes),
gsl::make_span(stringSetCache_.manipulatedValues))
gsl::make_span(realIndexes),
gsl::make_span(realValues),
gsl::make_span(integerIndexes),
gsl::make_span(integerValues),
gsl::make_span(booleanIndexes),
gsl::make_span(booleanValues),
gsl::make_span(stringIndexes),
gsl::make_span(stringValues))
.get();
realSetCache_.reset();
integerSetCache_.reset();
booleanSetCache_.reset();
stringSetCache_.reset();
}

void get_variables()
Expand All @@ -341,15 +443,15 @@ class slave_simulator::impl
std::string name_;
cse::model_description modelDescription_;

exposed_vars<double> realGetCache_;
exposed_vars<int> integerGetCache_;
exposed_vars<bool> booleanGetCache_;
exposed_vars<std::string> stringGetCache_;
get_variable_cache<double> realGetCache_;
get_variable_cache<int> integerGetCache_;
get_variable_cache<bool> booleanGetCache_;
get_variable_cache<std::string> stringGetCache_;

exposed_vars<double> realSetCache_;
exposed_vars<int> integerSetCache_;
exposed_vars<bool> booleanSetCache_;
exposed_vars<std::string> stringSetCache_;
set_variable_cache<double> realSetCache_;
set_variable_cache<int> integerSetCache_;
set_variable_cache<bool> booleanSetCache_;
set_variable_cache<std::string> stringSetCache_;
};


Expand Down
Loading