CYCLUS
Loading...
Searching...
No Matches
greedy_solver.cc
Go to the documentation of this file.
1#include "greedy_solver.h"
2
3#include <algorithm>
4#include <cassert>
5#include <functional>
6#include <vector>
7#include <boost/math/special_functions/next.hpp>
8
9#include "cyc_limits.h"
10#include "error.h"
11#include "logger.h"
12
13namespace cyclus {
14
15void Capacity(cyclus::Arc const&, double, double) {};
16void Capacity(boost::shared_ptr<cyclus::ExchangeNode>, cyclus::Arc const&,
17 double) {};
18
22
27
31
35
37 if (conditioner_ != NULL)
38 delete conditioner_;
39}
40
42 if (conditioner_ != NULL)
43 conditioner_->Condition(graph_);
44}
45
47 std::for_each(graph_->request_groups().begin(),
48 graph_->request_groups().end(),
49 std::bind(
50 &GreedySolver::GetCaps,
51 this,
52 std::placeholders::_1));
53
54 std::for_each(graph_->supply_groups().begin(),
55 graph_->supply_groups().end(),
56 std::bind(
57 &GreedySolver::GetCaps,
58 this,
59 std::placeholders::_1));
60}
61
63 double pseudo_cost = PseudoCost(); // from ExchangeSolver API
64 Condition();
65 obj_ = 0;
66 unmatched_ = 0;
67 n_qty_.clear();
68
69 Init();
70
71 std::for_each(graph_->request_groups().begin(),
72 graph_->request_groups().end(),
73 std::bind(
74 &GreedySolver::GreedilySatisfySet,
75 this,
76 std::placeholders::_1));
77
78 obj_ += unmatched_ * pseudo_cost;
79 return obj_;
80}
81
82double GreedySolver::Capacity(const Arc& a, double u_curr_qty,
83 double v_curr_qty) {
84 bool min = true;
85 double ucap = Capacity(a.unode(), a, !min, u_curr_qty);
86 double vcap = Capacity(a.vnode(), a, min, v_curr_qty);
87
88 CLOG(cyclus::LEV_DEBUG1) << "Capacity for unode of arc: " << ucap;
89 CLOG(cyclus::LEV_DEBUG1) << "Capacity for vnode of arc: " << vcap;
90 CLOG(cyclus::LEV_DEBUG1) << "Capacity for arc : "
91 << std::min(ucap, vcap);
92
93 return std::min(ucap, vcap);
94}
95
97 double curr_qty) {
98 if (n->group == NULL) {
99 throw cyclus::StateError("An notion of node capacity requires a nodegroup.");
100 }
101
102 if (n->unit_capacities[a].size() == 0) {
103 return n->qty - curr_qty;
104 }
105
106 std::vector<double>& unit_caps = n->unit_capacities[a];
107 const std::vector<double>& group_caps = grp_caps_[n->group];
108 std::vector<double> caps;
109 double grp_cap, u_cap, cap;
110
111 for (int i = 0; i < unit_caps.size(); i++) {
112 grp_cap = group_caps[i];
113 u_cap = unit_caps[i];
114 cap = grp_cap / u_cap;
115 CLOG(cyclus::LEV_DEBUG1) << "Capacity for node: ";
116 CLOG(cyclus::LEV_DEBUG1) << " group capacity: " << grp_cap;
117 CLOG(cyclus::LEV_DEBUG1) << " unit capacity: " << u_cap;
118 CLOG(cyclus::LEV_DEBUG1) << " capacity: " << cap;
119
120 // special case for unlimited capacities
121 if (grp_cap == std::numeric_limits<double>::max()) {
122 caps.push_back(std::numeric_limits<double>::max());
123 } else {
124 caps.push_back(cap);
125 }
126 }
127
128 if (min_cap) { // the smallest value is constraining (for bids)
129 cap = *std::min_element(caps.begin(), caps.end());
130 } else { // the largest value must be met (for requests)
131 cap = *std::max_element(caps.begin(), caps.end());
132 }
133 return std::min(cap, n->qty - curr_qty);
134}
135
136void GreedySolver::GetCaps(ExchangeNodeGroup::Ptr g) {
137 for (int i = 0; i != g->nodes().size(); i++) {
138 n_qty_[g->nodes()[i]] = 0;
139 }
140 grp_caps_[g.get()] = g->capacities();
141}
142
143void GreedySolver::GreedilySatisfySet(RequestGroup::Ptr prs) {
144 std::vector<ExchangeNode::Ptr>& nodes = prs->nodes();
145 std::stable_sort(nodes.begin(), nodes.end(), AvgPrefComp);
146
147 std::vector<ExchangeNode::Ptr>::iterator req_it = nodes.begin();
148 double target = prs->qty();
149 double match = 0;
150
152 std::vector<Arc>::const_iterator arc_it;
153 std::vector<Arc> sorted;
154 double remain, tomatch, excl_val;
155
156 CLOG(LEV_DEBUG1) << "Greedy Solving for " << target
157 << " amount of a resource.";
158
159 while ((match <= target) && (req_it != nodes.end())) {
160 // this if statement is needed because map.at() will throw if the key does
161 // not exist, which is a corner case for when there is a request with no bid
162 // arcs associated with it
163 if (graph_->node_arc_map().count(*req_it) > 0) {
164 const std::vector<Arc>& arcs = graph_->node_arc_map().at(*req_it);
165 sorted = std::vector<Arc>(arcs); // make a copy for now
166 std::stable_sort(sorted.begin(), sorted.end(), ReqPrefComp);
167 arc_it = sorted.begin();
168
169 while ((match <= target) && (arc_it != sorted.end())) {
170 remain = target - match;
171 const Arc& a = *arc_it;
172 u = a.unode();
173 v = a.vnode();
174 // capacity adjustment
175 tomatch = std::min(remain, Capacity(a, n_qty_[u], n_qty_[v]));
176
177 // exclusivity adjustment
178 if (arc_it->exclusive()) {
179 excl_val = a.excl_val();
180
181 // this careful float comparison is vital for preventing false positive
182 // constraint violations w.r.t. exclusivity-related capacity.
183 double dist = boost::math::float_distance(tomatch, excl_val);
184 if (dist >= float_ulp_eq ) {
185 tomatch = 0;
186 } else {
187 tomatch = excl_val;
188 }
189 }
190
191 if (tomatch > eps()) {
192 CLOG(LEV_DEBUG1) << "Greedy Solver is matching " << tomatch
193 << " amount of a resource.";
194 UpdateCapacity(u, a, tomatch);
195 UpdateCapacity(v, a, tomatch);
196 n_qty_[u] += tomatch;
197 n_qty_[v] += tomatch;
199
200 match += tomatch;
201 UpdateObj(tomatch, u->prefs[a]);
202 }
203 ++arc_it;
204 } // while( (match =< target) && (arc_it != arcs.end()) )
205 } // if(graph_->node_arc_map().count(*req_it) > 0)
206 ++req_it;
207 } // while( (match =< target) && (req_it != nodes.end()) )
208
209 unmatched_ += target - match;
210}
211
212void GreedySolver::UpdateObj(double qty, double pref) {
213 // updates minimizing object (i.e., 1/pref is a cost and the objective is cost
214 // * flow)
215 obj_ += qty / pref;
216}
217
218void GreedySolver::UpdateCapacity(ExchangeNode::Ptr n, const Arc& a,
219 double qty) {
220 using cyclus::IsNegative;
221 using cyclus::ValueError;
222
223 std::vector<double>& unit_caps = n->unit_capacities[a];
224 std::vector<double>& caps = grp_caps_[n->group];
225 assert(unit_caps.size() == caps.size());
226 for (int i = 0; i < caps.size(); i++) {
227 double prev = caps[i];
228 // special case for unlimited capacities
229 CLOG(cyclus::LEV_DEBUG1) << "Updating capacity value from: "
230 << prev;
231 caps[i] = (prev == std::numeric_limits<double>::max()) ?
232 std::numeric_limits<double>::max() :
233 prev - qty * unit_caps[i];
234 CLOG(cyclus::LEV_DEBUG1) << " to: "
235 << caps[i];
236 }
237
238 if (IsNegative(n->qty - qty)) {
239 std::stringstream ss;
240 ss << "A bid for " << n->commod << " was set at " << n->qty
241 << " but has been matched to a higher value " << qty
242 << ". This could be due to a problem with your "
243 << "bid portfolio constraints.";
244 throw ValueError(ss.str());
245 }
246}
247
248} // namespace cyclus
An arc represents a possible connection between two nodes in the bipartite resource exchange graph.
const std::vector< ExchangeNodeGroup::Ptr > & supply_groups() const
const std::map< ExchangeNode::Ptr, std::vector< Arc > > & node_arc_map() const
const std::vector< RequestGroup::Ptr > & request_groups() const
void AddMatch(const Arc &a, double qty)
adds a match for a quanity of flow along an arc
boost::shared_ptr< ExchangeNodeGroup > Ptr
a very simple interface for solving translated resource exchanges
double PseudoCost()
Calculates the ratio of the maximum objective coefficient to minimum unit capacity plus an added cost...
A GreedyPreconditioner conditions an ExchangeGraph for a GreedySolver by ordering the RequestGroups a...
void Condition(ExchangeGraph *graph)
conditions the graph as described above
virtual double SolveGraph()
the GreedySolver solves an ExchangeGraph by iterating over each RequestGroup and matching requests wi...
GreedySolver()
GreedySolver constructor.
double Capacity(const Arc &a, double u_curr_qty, double v_curr_qty)
the capacity of the arc
void Condition()
Uses the provided (or a default) GreedyPreconditioner to condition the solver's ExchangeGraph so that...
void Init()
Initialize member values based on the given graph.
boost::shared_ptr< RequestGroup > Ptr
For failed object state expectations.
Definition error.h:53
For values that are too big, too small, etc.
Definition error.h:41
Code providing rudimentary logging capability for the Cyclus core.
#define CLOG(level)
Definition logger.h:39
taken directly from OsiSolverInterface.cpp on 2/17/14 from https://projects.coin-or....
Definition agent.cc:14
void Capacity(cyclus::Arc const &, double, double)
bool IsNegative(double d)
returns true if a double is less than 0 - eps()
Definition cyc_limits.h:24
bool AvgPrefComp(ExchangeNode::Ptr l, ExchangeNode::Ptr r)
A comparison function for sorting a container of Nodes by the nodes preference in decensing order (i....
bool ReqPrefComp(const Arc &l, const Arc &r)
double Capacity(const Arc& a, double u_curr_qty, double v_curr_qty) { return 0; }
@ LEV_DEBUG1
debugging information - least verbose
Definition logger.h:58
double eps()
a generic epsilon value
Definition cyc_limits.h:12
static const double float_ulp_eq
distance in ULP within which floating point numbers should be considered equal.
Definition cyc_limits.h:35
T OptionalQuery(InfileTree *tree, std::string query, T default_val)
a query method for optional parameters
boost::shared_ptr< ExchangeNode > Ptr