sigma  1.0.0
Loading...
Searching...
No Matches
setter.hpp
Go to the documentation of this file.
1#pragma once
3#include <vector>
4
8
9namespace sigma::detail_ {
10
19template<typename UncertainType>
20class Setter {
21public:
24
26 using uncertain_t = UncertainType;
27
29 using value_t = typename uncertain_t::value_t;
30
32 using dep_sd_t = typename uncertain_t::dep_sd_t;
33
35 using dep_sd_ptr = typename uncertain_t::dep_sd_ptr;
36
38 using deps_map_t = typename uncertain_t::deps_map_t;
39
41 using deps_vector_t = typename std::vector<dep_sd_ptr>;
42
49 Setter(uncertain_t& u) : m_x_(u) {}
50
61 void update_mean(value_t mean) {
62 if(std::abs(m_x_.m_mean_ - mean) >= m_x_.threshold())
63 m_x_.m_mean_ = mean;
64 }
65
72 m_x_.m_sd_ = 0.0;
73 for(const auto& [dep, deriv] : m_x_.m_deps_) {
74 m_x_.m_sd_ += std::pow(*dep.get() * deriv, 2.0);
75 }
76 m_x_.m_sd_ = std::sqrt(m_x_.m_sd_);
77 }
78
91 // If the derivative is effectively zero, we can just clear the
92 // dependencies and set the standard deviation to zero.
93 if(std::abs(dxda) < m_x_.threshold() || dxda == 0.0) {
94 m_x_.m_deps_.clear();
95 m_x_.m_sd_ = 0.0;
96 return;
97 }
98 // If the derivative is one, we don't need to do anything since the
99 // derivatives are unchanged.
100 if(dxda == 1.0) return;
101 // Update the derivatives in place, tracking any that are reduced below
102 // our threshold and will need to be removed from the dependencies map.
103 m_removed_deps_.clear();
104 for(const auto& [dep, deriv] : m_x_.m_deps_) {
105 m_x_.m_deps_[dep] *= dxda;
106 if(std::abs(m_x_.m_deps_[dep]) < m_x_.threshold()) {
107 m_removed_deps_.emplace_back(dep);
108 }
109 }
110 // If no dependencies need to be removed, we can just update the
111 // standard deviation by multiplying by the absolute value of the
112 // partial derivative. If the partial derivate is negative one, we don't
113 // need to update the standard deviation since it is unchanged. If any
114 // dependencies were reduced below our threshold, we need to remove them
115 // from the dependencies map and recalculate the standard deviation.
116 if(m_removed_deps_.size() == 0) {
117 if(dxda != -1.0) m_x_.m_sd_ *= std::abs(dxda);
118 } else {
119 for(const auto& dep : m_removed_deps_) m_x_.m_deps_.erase(dep);
121 }
122 }
123
138 void update_derivatives(const deps_map_t& deps, value_t dxda) {
139 // If the derivative is zero, we can skip the update since it won't
140 // change anything.
141 if(std::abs(dxda) < m_x_.threshold() || dxda == 0.0) { return; }
142 // Update the map of contributions and their derivatives, keeping track
143 // of any that are reduced to zero and will need to be removed. If the
144 // uncertainty or contribution of a new dependency is below the
145 // threshold, we can skip adding it to the map.
146 m_removed_deps_.clear();
147 size_t n_updated = 0;
148 for(const auto& [dep, deriv] : deps) {
149 if(m_x_.m_deps_.count(dep)) {
150 m_x_.m_deps_[dep] += dxda * deriv;
151 if(std::abs(m_x_.m_deps_[dep]) < m_x_.threshold() ||
152 m_x_.m_deps_[dep] == 0.0) {
153 m_removed_deps_.emplace_back(dep);
154 }
155 n_updated++;
156 } else {
157 if(std::abs(dxda * deriv) < m_x_.threshold()) continue;
158 if(std::abs(*dep.get()) < m_x_.threshold()) continue;
159 m_x_.m_deps_.emplace(dep, dxda * deriv);
160 }
161 }
162 // If more than half of the existing dependencies are being updated,
163 // it's more efficient to update the derivatives and then recalculate
164 // the standard deviation from scratch. Otherwise, we can update the
165 // standard deviation in place by removing the contribution of the
166 // zeroed and updated dependencies and then adding the new contribution
167 // of the updated dependencies.
168 if(n_updated > (m_x_.m_deps_.size() / 2)) {
169 for(const auto& dep : m_removed_deps_) m_x_.m_deps_.erase(dep);
171 } else {
172 // Return to the variance so we can update the sum.
173 m_x_.m_sd_ = std::pow(m_x_.m_sd_, 2.0);
174 // Step through the updated dependencies and update the variance.
175 // accordingly.
176 for(const auto& [dep, deriv] : deps) {
177 // Skip any dependencies that aren't in the map at this point.
178 if(!m_x_.m_deps_.count(dep)) continue;
179 // Calculate the previous value of the derivative for this
180 // dependency.
181 auto old_deriv = m_x_.m_deps_[dep] - dxda * deriv;
182 // As long as the dependency hasn't been reduce to zero, we need
183 // to add its contribution to the variance.
184 if(m_x_.m_deps_[dep] != 0.0) {
185 m_x_.m_sd_ += std::pow(*dep.get() * m_x_.m_deps_[dep], 2.0);
186 }
187 // As long as the dependency isn't new, we need to remove its
188 // previous contribution to the variance.
189 if(old_deriv != 0.0) {
190 m_x_.m_sd_ -= std::pow(*dep.get() * old_deriv, 2.0);
191 }
192 }
193 m_x_.m_sd_ = std::sqrt(m_x_.m_sd_);
194 for(const auto& dep : m_removed_deps_) m_x_.m_deps_.erase(dep);
195 }
196 }
197
198private:
200 uncertain_t& m_x_;
201
204 deps_vector_t m_removed_deps_{};
205};
206
207} // namespace sigma::detail_
UncertainType uncertain_t
The numeric type of the variable.
Definition setter.hpp:26
void update_derivatives(value_t dxda)
Update of existing derivatives.
Definition setter.hpp:90
typename uncertain_t::dep_sd_ptr dep_sd_ptr
A pointer to a dependency of this variable.
Definition setter.hpp:35
void update_derivatives(const deps_map_t &deps, value_t dxda)
Update/addition of derivatives.
Definition setter.hpp:138
Setter(uncertain_t &u)
Construct a Setter for a variable.
Definition setter.hpp:49
Setter< UncertainType > my_t
Type of the instance.
Definition setter.hpp:23
void update_mean(value_t mean)
Update the mean of the wrapped variable.
Definition setter.hpp:61
typename uncertain_t::value_t value_t
The type of the values of the variable.
Definition setter.hpp:29
typename uncertain_t::dep_sd_t dep_sd_t
The type of a standard deviation that this instance depends on.
Definition setter.hpp:32
typename uncertain_t::deps_map_t deps_map_t
The type of the map holding the variable's dependencies.
Definition setter.hpp:38
void recalculate_sd()
Recalculate the standard deviation of the wrapped variable from its dependencies.
Definition setter.hpp:71
typename std::vector< dep_sd_ptr > deps_vector_t
The type of a vector of dependencies of this variable.
Definition setter.hpp:41
The namespace that contains the implementation details of the library.
Defines the Uncertain class.