fts_solver/types/submission.rs
1use super::{Auth, Constant, Cost, Group, PiecewiseLinearCurve};
2use crate::{Map, Set};
3use std::hash::Hash;
4
5/// A submission is a full expression of an agent's preferences in an auction.
6#[derive(Debug)]
7pub struct Submission<AuthId: Eq + Hash, ProductId: Eq + Hash> {
8 pub(crate) auths: Map<AuthId, Auth<ProductId>>,
9 pub(crate) cost_curves: Vec<(Group<AuthId>, PiecewiseLinearCurve)>,
10 pub(crate) cost_constants: Vec<(Group<AuthId>, Constant)>,
11}
12
13impl<AuthId: Eq + Hash + Clone, ProductId: Eq + Hash + Clone> Submission<AuthId, ProductId> {
14 /// Creates a new submission from the provided auths and costs.
15 /// All data is assumed to be valid.
16 pub fn new<
17 A: IntoIterator<Item = (AuthId, Auth<ProductId>)>,
18 B: IntoIterator<Item = (Group<AuthId>, Cost)>,
19 >(
20 auths: A,
21 costs: B,
22 ) -> Option<Self> {
23 // We filter the auths down to only those that allow for non-zero trade.
24 // We will later apply an additional filter that further removes any auths
25 // that are not otherwise referenced by a cost.
26 let mut auths = auths
27 .into_iter()
28 .filter(
29 |(
30 _,
31 Auth {
32 min_trade,
33 max_trade,
34 portfolio,
35 },
36 )| {
37 (*min_trade < 0.0 || *max_trade > 0.0) && portfolio.len() > 0
38 },
39 )
40 .collect::<Map<AuthId, Auth<ProductId>>>();
41
42 // We also will partition the submission's costs into the various
43 // supported types of cost specification.
44 let mut curves = Vec::new();
45 let mut constants = Vec::new();
46
47 // This will allow us to track if auths are referenced by costs or not
48 let mut in_use = Set::<AuthId>::default();
49
50 // 2. Partition the costs by type, as well as record which auths are referenced.
51 for (group, cost) in costs.into_iter() {
52 // Record any auths in use (the keys() method of group will not return zero-valued entries)
53 in_use.extend(group.keys().map(|x| x.clone()));
54 // Then we just partition into the relevant bins. If we ever support
55 // additional cost curve types, we can just add another field to the Submission
56 // and handle the partitioning here.
57 match cost {
58 Cost::PiecewiseLinearCurve(curve) => {
59 curves.push((group, curve));
60 }
61 Cost::Constant(constant) => {
62 constants.push((group, constant));
63 }
64 }
65 }
66
67 // 2. Remove any authorizations not otherwise referenced.
68 // THIS IS VERY IMPORTANT, as otherwise we may end up creating an unconstrained decision variable
69 auths.retain(|id, _| in_use.contains(id));
70
71 // 3. We now "deflate" the groups to eliminate stale auth references
72 for group in curves
73 .iter_mut()
74 .map(|(group, _)| group)
75 .chain(constants.iter_mut().map(|(group, _)| group))
76 {
77 group.retain(|key, _| auths.get(key).is_some())
78 }
79
80 // 4. Remove any curves, constants with empty groups
81 curves.retain(|(group, _)| group.len() > 0);
82 constants.retain(|(group, _)| group.len() > 0);
83
84 if curves.len() > 0 || constants.len() > 0 {
85 Some(Submission {
86 auths,
87 cost_curves: curves,
88 cost_constants: constants,
89 })
90 } else {
91 None
92 }
93 }
94}