use crate::{HashSet, Segment, disaggregate};
use fts_core::models::{DemandCurve, DemandGroup, Map, ProductGroup};
use std::fmt::Display;
use std::hash::Hash;
use std::io::Write;
pub fn export_mps<
DemandId: Display + Eq + Hash + Clone,
PortfolioId: Display + Eq + Hash + Clone,
ProductId: Display + Eq + Hash + Clone + Ord,
>(
demand_curves: Map<DemandId, DemandCurve>,
portfolios: Map<PortfolioId, (DemandGroup<DemandId>, ProductGroup<ProductId>)>,
buffer: &mut impl Write,
) -> Result<(), std::io::Error> {
let (demand_curves, portfolios, _, products) = super::prepare(demand_curves, portfolios);
writeln!(buffer, "NAME flow_trade_qp")?;
writeln!(buffer, "ROWS")?;
writeln!(buffer, " N gft")?;
for product_id in products.keys() {
writeln!(buffer, " E p_{product_id}")?;
}
for demand_id in demand_curves.keys() {
writeln!(buffer, " E d_{demand_id}")?;
}
writeln!(buffer, "COLUMNS")?;
let mut zeroed = HashSet::default();
for (portfolio_id, (demand_weights, product_weights)) in portfolios.iter() {
for (product_id, weight) in product_weights.iter() {
writeln!(buffer, " x_{portfolio_id} p_{product_id} {weight}",)?;
}
for (demand_id, weight) in demand_weights.iter() {
writeln!(buffer, " x_{portfolio_id} d_{demand_id} {weight}",)?;
}
if product_weights.len() == 0 || demand_weights.len() == 0 {
zeroed.insert(portfolio_id);
}
}
let mut all_segments = Map::<(DemandId, usize), Segment>::default();
for (demand_id, demand_curve) in demand_curves.into_iter() {
let (min, max) = demand_curve.domain();
let points = demand_curve.points();
let segments = disaggregate(points.into_iter(), min, max).expect("empty demand curve");
for (idx, segment) in segments.enumerate() {
let segment = segment.unwrap();
let (_, b) = segment.slope_intercept();
writeln!(
buffer,
" y_{demand_id}_{idx} gft {term} d_{demand_id} -1",
term = -b
)?;
all_segments.insert((demand_id.clone(), idx), segment);
}
}
writeln!(buffer, "BOUNDS")?;
for portfolio_id in portfolios.keys() {
if zeroed.contains(portfolio_id) {
writeln!(buffer, " FX BND x_{portfolio_id} 0")?;
} else {
writeln!(buffer, " FR BND x_{portfolio_id}")?;
}
}
for ((demand_id, idx), segment) in all_segments.iter() {
let min = segment.q0;
let max = segment.q1;
if min.is_finite() {
writeln!(buffer, " LO BND y_{demand_id}_{idx} {min}",)?;
} else {
writeln!(buffer, " MI BND y_{demand_id}_{idx}",)?;
}
if max.is_finite() {
writeln!(buffer, " UP BND y_{demand_id}_{idx} {max}",)?;
} else {
writeln!(buffer, " PL BND y_{demand_id}_{idx}",)?;
}
}
writeln!(buffer, "QUADOBJ")?;
for ((demand_id, idx), segment) in all_segments {
let (m, _) = segment.slope_intercept();
writeln!(
buffer,
" y_{demand_id}_{idx} y_{demand_id}_{idx} {term}",
term = -m
)?;
}
writeln!(buffer, "ENDATA")?;
Ok(())
}
pub fn export_lp<
DemandId: Display + Eq + Hash + Clone,
PortfolioId: Display + Eq + Hash + Clone,
ProductId: Display + Eq + Hash + Clone + Ord,
>(
demand_curves: Map<DemandId, DemandCurve>,
portfolios: Map<PortfolioId, (DemandGroup<DemandId>, ProductGroup<ProductId>)>,
buffer: &mut impl Write,
) -> Result<(), std::io::Error> {
let (demand_curves, portfolios, _, products) = super::prepare(demand_curves, portfolios);
let mut all_segments = Map::<(DemandId, usize), Segment>::default();
writeln!(buffer, "Maximize")?;
write!(buffer, " gft: ")?;
let mut first_term = true;
let mut has_quadratic_terms = false;
for (demand_id, demand_curve) in demand_curves.iter() {
let (min, max) = demand_curve.domain();
let points = demand_curve.clone().points();
let segments = disaggregate(points.into_iter(), min, max).expect("empty demand curve");
for (idx, segment) in segments.enumerate() {
let segment = segment.unwrap();
let (m, b) = segment.slope_intercept();
has_quadratic_terms = has_quadratic_terms || m != 0.0;
if first_term {
write!(buffer, "{b} y_{demand_id}_{idx}")?;
first_term = false;
} else {
write!(buffer, " + {b} y_{demand_id}_{idx}")?;
}
all_segments.insert((demand_id.clone(), idx), segment);
}
}
if first_term {
write!(buffer, "0")?;
}
if has_quadratic_terms {
write!(buffer, " + [ ")?;
first_term = true;
for ((demand_id, idx), segment) in all_segments.iter() {
let (m, _) = segment.slope_intercept();
if first_term {
write!(buffer, "{m} y_{demand_id}_{idx} ^ 2")?;
first_term = false;
} else {
write!(buffer, " + {m} y_{demand_id}_{idx} ^ 2",)?;
}
}
write!(buffer, " ] / 2")?;
}
writeln!(buffer)?;
writeln!(buffer, "Subject To")?;
for product_id in products.keys() {
write!(buffer, " p_{product_id}: ")?;
let mut first_term = true;
for (portfolio_id, (_, product_weights)) in portfolios.iter() {
if let Some(&weight) = product_weights.get(product_id) {
if first_term {
write!(buffer, "{weight} x_{portfolio_id}")?;
first_term = false;
} else {
write!(buffer, " + {weight} x_{portfolio_id}",)?;
}
}
}
if first_term {
write!(buffer, "0")?;
}
writeln!(buffer, " = 0")?;
}
for demand_id in demand_curves.keys() {
write!(buffer, " d_{demand_id}: ")?;
let mut first_term = true;
for (portfolio_id, (demand_weights, _)) in portfolios.iter() {
if let Some(&weight) = demand_weights.get(demand_id) {
if first_term {
write!(buffer, "{weight} x_{portfolio_id}")?;
first_term = false;
} else {
write!(buffer, " + {weight} x_{portfolio_id}",)?;
}
}
}
let mut key = (demand_id.clone(), 0);
while all_segments.contains_key(&key) {
write!(buffer, " - y_{demand_id}_{idx}", idx = key.1)?;
key.1 += 1;
}
writeln!(buffer, " = 0")?;
}
writeln!(buffer, "Bounds")?;
for (portfolio_id, (a, b)) in portfolios.iter() {
if a.len() == 0 || b.len() == 0 {
writeln!(buffer, " x_{portfolio_id} = 0")?;
} else {
writeln!(buffer, " x_{portfolio_id} free")?;
}
}
for ((demand_id, idx), segment) in all_segments {
match (segment.q0.is_finite(), segment.q1.is_finite()) {
(true, true) => {
writeln!(
buffer,
" {min} <= y_{demand_id}_{idx} <= {max}",
min = segment.q0,
max = segment.q1
)?;
}
(true, false) => {
writeln!(buffer, " y_{demand_id}_{idx} >= {min}", min = segment.q0)?;
}
(false, true) => {
writeln!(buffer, " y_{demand_id}_{idx} <= {max}", max = segment.q1)?;
}
(false, false) => {
writeln!(buffer, " y_{demand_id}_{idx} free")?;
}
}
}
writeln!(buffer, "End")?;
Ok(())
}