problemreductions/rules/
ilp_casts.rs1use crate::models::algebraic::{Comparison, LinearConstraint, VariableDomain, ILP};
4use crate::reduction;
5use crate::rules::{ReduceTo, ReductionError, ReductionResult};
6use crate::types::i64_to_exact_f64;
7
8#[derive(Debug, Clone)]
9pub struct ReductionILPToFloat<V: VariableDomain> {
10 source: ILP<V>,
11 target: ILP<V, f64>,
12}
13
14impl<V: VariableDomain> ReductionILPToFloat<V> {
15 fn new(source: &ILP<V>) -> Result<Self, ReductionError> {
16 let convert = |coefficient: i64| {
17 i64_to_exact_f64(coefficient)
18 .map_err(ReductionError::inexact_float_conversion::<ILP<V>, ILP<V, f64>>)
19 };
20 let constraints = source
21 .constraints()
22 .iter()
23 .map(|constraint| {
24 let terms = constraint
25 .terms()
26 .iter()
27 .map(|&(variable, coefficient)| Ok((variable, convert(coefficient)?)))
28 .collect::<Result<Vec<_>, ReductionError>>()?;
29 let rhs = convert(constraint.rhs())?;
30 Ok(match constraint.comparison() {
31 Comparison::Le => LinearConstraint::le(terms, rhs),
32 Comparison::Ge => LinearConstraint::ge(terms, rhs),
33 Comparison::Eq => LinearConstraint::eq(terms, rhs),
34 })
35 })
36 .collect::<Result<Vec<_>, ReductionError>>()?;
37 let objective = source
38 .objective()
39 .iter()
40 .map(|&(variable, coefficient)| Ok((variable, convert(coefficient)?)))
41 .collect::<Result<Vec<_>, ReductionError>>()?;
42 let target = ILP::with_variables(
43 source.variables().to_vec(),
44 constraints,
45 objective,
46 source.sense(),
47 )
48 .map_err(ReductionError::construction::<ILP<V>, ILP<V, f64>>)?;
49 Ok(Self {
50 source: source.clone(),
51 target,
52 })
53 }
54}
55
56impl<V: VariableDomain> ReductionResult for ReductionILPToFloat<V> {
57 type Source = ILP<V>;
58 type Target = ILP<V, f64>;
59
60 fn target_problem(&self) -> &Self::Target {
61 &self.target
62 }
63
64 fn extract_solution(
65 &self,
66 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
67 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
68 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
69 if !self.source.is_feasible(target_solution)? {
70 return Err(crate::rules::ExtractionError::invalid(
71 "the floating-point assignment violates the source integer ILP",
72 ));
73 }
74 Ok(target_solution.clone())
75 }
76}
77
78#[reduction(
79 transform = exact {
80 num_vars = "num_vars",
81 num_constraints = "num_constraints",
82 num_nonzeros = "num_nonzeros",
83 },
84)]
85impl ReduceTo<ILP<bool, f64>> for ILP<bool> {
86 type Result = ReductionILPToFloat<bool>;
87
88 fn reduce_to(&self) -> Result<Self::Result, ReductionError> {
89 ReductionILPToFloat::new(self)
90 }
91}
92
93#[reduction(
94 transform = exact {
95 num_vars = "num_vars",
96 num_constraints = "num_constraints",
97 num_nonzeros = "num_nonzeros",
98 },
99)]
100impl ReduceTo<ILP<i64, f64>> for ILP<i64> {
101 type Result = ReductionILPToFloat<i64>;
102
103 fn reduce_to(&self) -> Result<Self::Result, ReductionError> {
104 ReductionILPToFloat::new(self)
105 }
106}
107
108#[cfg(test)]
109#[path = "../unit_tests/rules/ilp_casts.rs"]
110mod tests;