1use crate::{HashSet, Submission};
2use std::fmt::Display;
3use std::hash::Hash;
4use std::io::Write;
5
6pub fn export_mps<
9 T,
10 BidderId: Display + Eq + Hash + Clone + Ord,
11 PortfolioId: Display + Eq + Hash + Clone + Ord,
12 ProductId: Display + Eq + Hash + Clone + Ord,
13>(
14 auction: &T,
15 buffer: &mut impl Write,
16) -> Result<(), std::io::Error>
17where
18 for<'t> &'t T: IntoIterator<Item = (&'t BidderId, &'t Submission<PortfolioId, ProductId>)>,
19{
20 writeln!(buffer, "NAME flow_trade_qp")?;
25 writeln!(buffer, "ROWS")?;
26
27 writeln!(buffer, " N gft")?;
29
30 let mut products = auction
32 .into_iter()
33 .flat_map(|(_, submission)| submission.portfolios.values())
34 .flat_map(|portfolio| portfolio.keys())
35 .collect::<HashSet<_>>();
36 products.sort_unstable();
37 for product_id in products.iter() {
38 writeln!(buffer, " E p_{product_id}")?;
40 }
41
42 for (bidder_id, submission) in auction.into_iter() {
44 for (offset, _) in submission.demand_curves.iter().enumerate() {
46 writeln!(buffer, " E g_{bidder_id}_{offset}")?;
47 }
48 }
49
50 writeln!(buffer, "COLUMNS")?;
54
55 for (bidder_id, submission) in auction.into_iter() {
57 for (portfolio_id, portfolio) in submission.portfolios.iter() {
58 for (product_id, weight) in portfolio.iter() {
59 writeln!(
60 buffer,
61 " x_{bidder_id}_{portfolio_id} p_{product_id} {weight}",
62 )?;
63 }
64
65 for (offset, (group, _)) in submission.demand_curves.iter().enumerate() {
66 if let Some(weight) = group.get(portfolio_id) {
67 writeln!(
68 buffer,
69 " x_{bidder_id}_{portfolio_id} g_{bidder_id}_{offset} {weight}",
70 )?;
71 }
72 }
73 }
74 }
75
76 for (bidder_id, submission) in auction.into_iter() {
78 for (offset, (_, curve)) in submission.demand_curves.iter().enumerate() {
79 for (idx, segment) in curve.iter().enumerate() {
80 let (_, b) = segment.slope_intercept();
83 writeln!(
84 buffer,
85 " y_{bidder_id}_{offset}_{idx} gft {term} g_{bidder_id}_{offset} -1",
86 term = -b
87 )?;
88 }
89 }
90 }
91
92 writeln!(buffer, "BOUNDS")?;
94 for (bidder_id, submission) in auction.into_iter() {
95 for portfolio_id in submission.portfolios.keys() {
96 writeln!(buffer, " FR BND x_{bidder_id}_{portfolio_id}")?;
98 }
99 }
100 for (bidder_id, submission) in auction.into_iter() {
101 for (offset, (_, curve)) in submission.demand_curves.iter().enumerate() {
102 for (idx, segment) in curve.iter().enumerate() {
103 if segment.q0.is_finite() {
105 writeln!(
106 buffer,
107 " LO BND y_{bidder_id}_{offset}_{idx} {min}",
108 min = segment.q0,
109 )?;
110 } else {
111 writeln!(buffer, " MI BND y_{bidder_id}_{offset}_{idx}",)?;
112 }
113 if segment.q1.is_finite() {
114 writeln!(
115 buffer,
116 " UP BND y_{bidder_id}_{offset}_{idx} {max}",
117 max = segment.q1,
118 )?;
119 } else {
120 writeln!(buffer, " PL BND y_{bidder_id}_{offset}_{idx}",)?;
121 }
122 }
123 }
124 }
125
126 writeln!(buffer, "QUADOBJ")?;
128 for (bidder_id, submission) in auction.into_iter() {
129 for (offset, (_, curve)) in submission.demand_curves.iter().enumerate() {
130 for (idx, segment) in curve.iter().enumerate() {
131 let (m, _) = segment.slope_intercept();
132 writeln!(
133 buffer,
134 " y_{bidder_id}_{offset}_{idx} y_{bidder_id}_{offset}_{idx} {term}",
135 term = -m
136 )?;
137 }
138 }
139 }
140
141 writeln!(buffer, "ENDATA")?;
142 Ok(())
143}
144
145pub fn export_lp<
148 T,
149 BidderId: Display + Eq + Hash + Clone + Ord,
150 PortfolioId: Display + Eq + Hash + Clone + Ord,
151 ProductId: Display + Eq + Hash + Clone + Ord,
152>(
153 auction: &T,
154 buffer: &mut impl Write,
155) -> Result<(), std::io::Error>
156where
157 for<'t> &'t T: IntoIterator<Item = (&'t BidderId, &'t Submission<PortfolioId, ProductId>)>,
158{
159 writeln!(buffer, "Maximize")?;
161
162 write!(buffer, " gft: ")?;
164
165 let mut first_term = true;
167 let mut has_quadratic_terms = false;
168 for (bidder_id, submission) in auction.into_iter() {
170 for (offset, (_, curve)) in submission.demand_curves.iter().enumerate() {
171 for (idx, segment) in curve.iter().enumerate() {
172 let (m, b) = segment.slope_intercept();
173 has_quadratic_terms = has_quadratic_terms || m != 0.0;
174 if first_term {
175 write!(buffer, "{b} y_{bidder_id}_{offset}_{idx}")?;
176 first_term = false;
177 } else {
178 write!(buffer, " + {b} y_{bidder_id}_{offset}_{idx}")?;
179 }
180 }
181 }
182 }
183
184 if first_term {
186 write!(buffer, "0")?;
187 }
188
189 if has_quadratic_terms {
190 write!(buffer, " + [ ")?;
191
192 first_term = true;
194
195 for (bidder_id, submission) in auction.into_iter() {
196 for (offset, (_, curve)) in submission.demand_curves.iter().enumerate() {
197 for (idx, segment) in curve.iter().enumerate() {
198 let (m, _) = segment.slope_intercept();
199 if first_term {
200 write!(buffer, "{m} y_{bidder_id}_{offset}_{idx} ^ 2")?;
201 first_term = false;
202 } else {
203 write!(buffer, " + {m} y_{bidder_id}_{offset}_{idx} ^ 2",)?;
204 }
205 }
206 }
207 }
208
209 write!(buffer, " ] / 2")?;
210 }
211
212 writeln!(buffer)?;
214
215 writeln!(buffer, "Subject To")?;
217
218 let mut products = auction
220 .into_iter()
221 .flat_map(|(_, submission)| submission.portfolios.values())
222 .flat_map(|portfolio| portfolio.keys())
223 .collect::<HashSet<_>>();
224 products.sort_unstable();
225
226 for product_id in products {
227 write!(buffer, " p_{product_id}: ")?;
229
230 let mut first_term = true;
232
233 for (bidder_id, submission) in auction.into_iter() {
235 for (portfolio_id, portfolio) in submission.portfolios.iter() {
236 if let Some(&weight) = portfolio.get(product_id) {
237 if first_term {
238 write!(buffer, "{weight} x_{bidder_id}_{portfolio_id}")?;
239 first_term = false;
240 } else {
241 write!(buffer, " + {weight} x_{bidder_id}_{portfolio_id}",)?;
242 }
243 }
244 }
245 }
246
247 if first_term {
249 write!(buffer, "0")?;
250 }
251
252 writeln!(buffer, " = 0")?;
254 }
255
256 for (bidder_id, submission) in auction.into_iter() {
258 for (offset, (group, curve)) in submission.demand_curves.iter().enumerate() {
259 write!(buffer, " g_{bidder_id}_{offset}: ")?;
261
262 let mut first_term = true;
264
265 for (portfolio_id, &weight) in group.iter() {
267 if first_term {
268 write!(buffer, "{weight} x_{bidder_id}_{portfolio_id}")?;
269 first_term = false;
270 } else {
271 write!(buffer, " + {weight} x_{bidder_id}_{portfolio_id}",)?;
272 }
273 }
274
275 for (idx, _) in curve.iter().enumerate() {
279 write!(buffer, " - y_{bidder_id}_{offset}_{idx}")?;
280 }
281
282 writeln!(buffer, " = 0")?;
284 }
285 }
286
287 writeln!(buffer, "Bounds")?;
289
290 for (bidder_id, submission) in auction.into_iter() {
292 for portfolio_id in submission.portfolios.keys() {
293 writeln!(buffer, " x_{bidder_id}_{portfolio_id} free")?;
294 }
295 }
296
297 for (bidder_id, submission) in auction.into_iter() {
299 for (offset, (_, curve)) in submission.demand_curves.iter().enumerate() {
300 for (idx, segment) in curve.iter().enumerate() {
301 match (segment.q0.is_finite(), segment.q1.is_finite()) {
302 (true, true) => {
303 writeln!(
304 buffer,
305 " {min} <= y_{bidder_id}_{offset}_{idx} <= {max}",
306 min = segment.q0,
307 max = segment.q1
308 )?;
309 }
310 (true, false) => {
311 writeln!(
312 buffer,
313 " y_{bidder_id}_{offset}_{idx} >= {min}",
314 min = segment.q0
315 )?;
316 }
317 (false, true) => {
318 writeln!(
319 buffer,
320 " y_{bidder_id}_{offset}_{idx} <= {max}",
321 max = segment.q1
322 )?;
323 }
324 (false, false) => {
325 writeln!(buffer, " y_{bidder_id}_{offset}_{idx} free")?;
326 }
327 }
328 }
329 }
330 }
331
332 writeln!(buffer, "End")?;
334
335 Ok(())
336}