Skip to main content

problemreductions/rules/
ilp_casts.rs

1//! Numeric variant reductions for ILP.
2
3use 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;