uatlib
simulation.hpp
Go to the documentation of this file.
1 
4 #ifndef UAT_SIMULATION_HPP
5 #define UAT_SIMULATION_HPP
6 
7 #include <uat/agent.hpp>
8 
9 #include <deque>
10 #include <optional>
11 #include <random>
12 #include <vector>
13 
14 #include <cool/compose.hpp>
15 
16 namespace uat
17 {
18 
20 using factory_t = std::function<std::vector<any_agent>(uint_t, int)>;
21 
23 template <region_compatible R> struct trade_info_t
24 {
25  uint_t transaction_time;
26  uint_t from;
27  uint_t to;
28  R location;
29  uint_t time;
30  value_t value;
31 };
32 
34 constexpr auto no_owner = std::numeric_limits<uint_t>::max();
35 
36 namespace permit_private_status
37 {
38 
40 struct on_sale
41 {
46 };
47 
49 struct in_use
50 {
51  uint_t owner;
52 };
53 
56 {};
57 
58 } // namespace permit_private_status
59 
62 {
64  std::variant<permit_private_status::on_sale, permit_private_status::in_use, permit_private_status::out_of_limits> current;
65 
67  std::vector<trade_value_t> history;
68 };
69 
70 namespace agent_private_status
71 {
72 
74 struct inactive
75 {
76  id_t id;
77 };
78 
80 struct active
81 {
82  id_t id;
83  any_agent data;
84 };
85 
86 } // namespace agent_private_status
87 
89 using agent_private_status_t = std::variant<agent_private_status::inactive, agent_private_status::active>;
90 
93 {
94 public:
96  auto active_count() const -> uint_t;
97  auto active() const -> std::span<const id_t>;
98 
99  void insert(any_agent);
100  void update_active(std::vector<id_t>);
101  auto at(id_t) -> any_agent&;
102 
103 private:
104  uint_t first_id_ = 0u;
105  std::deque<any_agent> agents_;
106  std::vector<id_t> active_;
107 };
108 
111 
113 template <region_compatible R> using trade_callback_t = std::function<void(trade_info_t<R>)>;
114 
117 
118 namespace stop_criterion
119 {
120 
123 {};
124 
127 {
128  uint_t t;
129 };
130 } // namespace stop_criterion
131 
133 using stop_criterion_t = std::variant<stop_criterion::no_agents_t, stop_criterion::time_threshold_t>;
134 
136 template <region_compatible R> struct simulation_opts_t
137 {
139  std::optional<uint_t> time_window;
143  std::optional<uint_t> seed;
144 };
145 
151 template <region_compatible R> auto simulate(const simulation_opts_t<R>& opts = {}) -> void
152 {
153  std::mt19937 rnd(opts.seed ? *opts.seed : std::random_device{}());
154 
155  agents_private_status_t agents;
156  std::vector<id_t> keep_active;
157 
158  uint_t t0 = 0;
159 
160  std::deque<std::unordered_map<permit<R>, permit_private_status_t>> data;
161 
162  permit_private_status_t ool = {permit_private_status::out_of_limits{}, {}};
163  auto book = [&t0, &data, &ool, &opts](region_view loc, uint_t t) mutable -> permit_private_status_t& {
164  if (t < t0) // XXX agents can check the state at t0, however they should be prohibited to bid for.
165  return ool;
166  if (opts.time_window && t > t0 + 1 + *opts.time_window)
167  return ool;
168  while (t - t0 >= data.size())
169  data.emplace_back();
170  return data[t - t0][{loc.downcast<R>(), t}];
171  };
172 
173  auto safe_book = [&book](region_view loc, uint_t t) -> permit_private_status_t { return book(loc, t); };
174 
175  auto public_access = [&book](auto id) {
176  return [id = id, &book](region_view s, uint_t t) -> permit_public_status_t {
177  using namespace permit_private_status;
178  using namespace permit_public_status;
179  const auto& pstatus = book(s, t);
180  return std::visit(
181  cool::compose{
182  [](out_of_limits) -> permit_public_status_t { return unavailable{}; },
183  [&](in_use status) -> permit_public_status_t {
184  return status.owner == id ? permit_public_status_t{owned{}} : unavailable{};
185  },
186  [&](on_sale status) -> permit_public_status_t {
187  return status.owner == id ? permit_public_status_t{unavailable{}} : available{status.min_value, pstatus.history};
188  }},
189  pstatus.current);
190  };
191  };
192 
193  const auto stop = [&] {
194  using namespace stop_criterion;
195  return std::visit(cool::compose{
196  [&](no_agents_t) { return agents.active_count() == 0; },
197  [&](time_threshold_t th) { return t0 > th.t; },
198  },
199  opts.stop_criterion);
200  };
201 
202  do {
203  if (opts.simulation_callback)
204  opts.simulation_callback(t0, std::as_const(agents), permit_private_status_fn(safe_book));
205 
206  // Generate new agents
207  if (opts.factory) {
208  auto new_agents = opts.factory(t0, rnd());
209  for (auto& agent : new_agents)
210  agents.insert(std::move(agent));
211  }
212 
213  {
214  // Bid phase
215  std::vector<permit<R>> bids;
216  for (const auto id : agents.active()) {
217  auto bid = [&](region_view s, uint_t t, value_t v) -> bool {
218  if (t < t0)
219  return false;
220  using namespace permit_private_status;
221  const auto visitor = cool::compose{[](out_of_limits) { return false; }, [](in_use) { return false; },
222  [&](on_sale& status) {
223  if (v > status.min_value && v > status.highest_bid) {
224  if (status.highest_bidder == no_owner)
225  bids.emplace_back(s.downcast<R>(), t);
226  status.highest_bidder = id;
227  status.highest_bid = v;
228  }
229  return true;
230  }};
231  return std::visit(visitor, book(s, t).current);
232  };
233 
234  auto access = public_access(id);
235  agents.at(id).bid_phase(t0, bid_fn(bid), permit_public_status_fn(access), rnd());
236  }
237 
238  // Trading
239  if (bids.size() > 0) {
240  const auto first_active = agents.active().front();
241  for (const auto& [s, t] : bids) {
242  const auto status = std::get<permit_private_status::on_sale>(book(s, t).current);
243  if (opts.trade_callback)
244  opts.trade_callback({t0, status.owner, status.highest_bidder, s, t, status.highest_bid});
245 
246  agents.at(status.highest_bidder).on_bought(s, t, status.highest_bid);
247  if (status.owner != no_owner && status.owner >= first_active)
248  agents.at(status.owner).on_sold(s, t, status.highest_bid);
249 
250  auto& pstatus = book(s, t);
251  pstatus.current = permit_private_status::in_use{status.highest_bidder};
252  pstatus.history.push_back({status.min_value, status.highest_bid});
253  }
254  }
255  }
256 
257  // Ask phase
258  {
259  std::vector<std::tuple<R, uint_t, uint_t, value_t>> asks;
260  for (const auto id : agents.active()) {
261  auto ask = [&](region_view s, uint_t t, value_t v) -> bool {
262  if (t < t0)
263  return false;
264  using namespace permit_private_status;
265  const auto visitor = cool::compose{[](out_of_limits) { return false; },
266  [&](on_sale status) {
267  if (status.owner != id)
268  return false;
269  asks.emplace_back(s.downcast<R>(), t, id, v);
270  return true;
271  },
272  [&](in_use& status) {
273  if (status.owner != id)
274  return false;
275  asks.emplace_back(s.downcast<R>(), t, id, v);
276  return true;
277  }};
278  return std::visit(visitor, book(s, t).current);
279  };
280 
281  auto access = public_access(id);
282  agents.at(id).ask_phase(t0, ask_fn(ask), permit_public_status_fn(access), rnd());
283  }
284 
285  for (const auto& [s, t, id, v] : asks)
286  book(s, t).current = permit_private_status::on_sale{.owner = id, .min_value = v};
287  }
288 
289  // Stop condition
290  keep_active.clear();
291  keep_active.reserve(agents.active_count());
292  for (const auto id : agents.active())
293  if (!agents.at(id).stop(t0, rnd()))
294  keep_active.push_back(id);
295  agents.update_active(std::move(keep_active));
296 
297  if (data.size() > 0)
298  data.pop_front();
299  ++t0;
300  } while (!stop());
301 }
302 
303 } // namespace uat
304 
305 #endif // UAT_SIMULATION_HPP
Defines the agent class and related types.
type_safe::function_ref< bool(region_view, uint_t, value_t)> bid_fn
Function reference that allows the agent to bid for a permit.
Definition: agent.hpp:52
type_safe::function_ref< bool(region_view, uint_t, value_t)> ask_fn
Function reference that allows the agent to ask for a permit.
Definition: agent.hpp:55
type_safe::function_ref< permit_public_status_t(region_view, uint_t)> permit_public_status_fn
Function reference that returns the public status of a permit.
Definition: agent.hpp:58
std::variant< permit_public_status::unavailable, permit_public_status::available, permit_public_status::owned > permit_public_status_t
Variant that represents the possible public status of a permit.
Definition: agent.hpp:49
Private status of the collection of agents in the simulation.
Definition: simulation.hpp:93
auto status(id_t) const -> agent_private_status_t
Get the private status of an agent with the given id.
auto active_count() const -> uint_t
Get the number of active agents.
auto active() const -> std::span< const id_t >
Get the ids of the active agents.
A type-erased class that represents an agent in the simulation.
Definition: agent.hpp:149
A non-owning wrapper to an atomic region in the airspace.
Definition: permit.hpp:21
std::variant< stop_criterion::no_agents_t, stop_criterion::time_threshold_t > stop_criterion_t
Variant that represents the possible stop criteria for the simulation.
Definition: simulation.hpp:133
auto simulate(const simulation_opts_t< R > &opts={}) -> void
Definition: simulation.hpp:151
std::function< void(trade_info_t< R >)> trade_callback_t
Callback type that receives information about a trade transaction.
Definition: simulation.hpp:113
constexpr auto no_owner
Unique value to represent the absence of an owner.
Definition: simulation.hpp:34
std::function< std::vector< any_agent >(uint_t, int)> factory_t
A function type that generates agents for each iteration.
Definition: simulation.hpp:20
std::function< void(uint_t, const agents_private_status_t &, permit_private_status_fn)> simulation_callback_t
Callback type that receives information about the status of the simulation.
Definition: simulation.hpp:116
type_safe::function_ref< permit_private_status_t(region_view, uint_t)> permit_private_status_fn
Function reference that allows the simulation to access the private status of an agent.
Definition: simulation.hpp:110
std::variant< agent_private_status::inactive, agent_private_status::active > agent_private_status_t
Variant that represents the private status of an agent.
Definition: simulation.hpp:89
Represents the private status of an agent that is active in the simulation.
Definition: simulation.hpp:81
Represents the private status of an agent that is inactive in the simulation.
Definition: simulation.hpp:75
Represents the private status of a permit that is not available for trading.
Definition: simulation.hpp:50
Represents the private status of a permit that is available for trading.
Definition: simulation.hpp:41
uint_t highest_bidder
The current highest bidder.
Definition: simulation.hpp:44
value_t min_value
The minimum value (exclusive) that the owner is willing to sell the permit.
Definition: simulation.hpp:43
uint_t owner
The owner of the permit.
Definition: simulation.hpp:42
value_t highest_bid
The current highest bid.
Definition: simulation.hpp:45
Represents the private status of a permit that is out of limits (past and future).
Definition: simulation.hpp:56
Represents the private status of a permit.
Definition: simulation.hpp:62
std::variant< permit_private_status::on_sale, permit_private_status::in_use, permit_private_status::out_of_limits > current
The current status of the permit.
Definition: simulation.hpp:64
std::vector< trade_value_t > history
The history of trades involving the permit.
Definition: simulation.hpp:67
Options to configure the simulation.
Definition: simulation.hpp:137
simulation_callback_t simulation_callback
Callback to receive information about the status of the simulation.
Definition: simulation.hpp:142
stop_criterion_t stop_criterion
The criterion to stop the simulation.
Definition: simulation.hpp:140
std::optional< uint_t > time_window
Maximum time ahead a permit can be traded.
Definition: simulation.hpp:139
std::optional< uint_t > seed
Random seed.
Definition: simulation.hpp:143
trade_callback_t< R > trade_callback
Callback to receive information about a trade transaction.
Definition: simulation.hpp:141
factory_t factory
Generator of agents for each iteration.
Definition: simulation.hpp:138
Stop criterion that stops the simulation when there are no agents.
Definition: simulation.hpp:123
Stop criterion that stops the simulation when a time threshold is reached.
Definition: simulation.hpp:127
Type to represent the information in a trade transaction.
Definition: simulation.hpp:24
std::size_t uint_t
Default unsigned integer type.
Definition: type.hpp:29
std::size_t id_t
Default type for identifier.
Definition: type.hpp:32
double value_t
Default type for price.
Definition: type.hpp:35