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}