Skip to main content

otspot_model/
expression.rs

1//! Expression type and operator overloads for the modeling API
2
3use std::collections::HashMap;
4use std::ops::{Add, Mul, Neg, Sub};
5
6use super::constraint::{Constraint, ConstraintSense};
7use super::variable::Variable;
8
9/// A linear expression: sum of (coefficient * variable) + constant.
10///
11/// Internally stored as a flat `HashMap<Variable, f64>` plus a scalar constant.
12/// This representation automatically merges like terms: `x + 2*x` → `{x: 3.0}`.
13#[derive(Debug, Clone, Default)]
14pub struct Expression {
15    pub(crate) coefficients: HashMap<Variable, f64>,
16    pub(crate) constant: f64,
17}
18
19impl Expression {
20    /// Create an expression representing a single scalar constant.
21    pub fn from_constant(c: f64) -> Self {
22        Expression {
23            coefficients: HashMap::new(),
24            constant: c,
25        }
26    }
27
28    /// Retrieve the coefficient for a given variable (0.0 if not present).
29    pub(crate) fn coefficient(&self, var: Variable) -> f64 {
30        self.coefficients.get(&var).copied().unwrap_or(0.0)
31    }
32
33    /// Merge another expression into this one (in-place add).
34    fn merge_add(&mut self, rhs: Expression) {
35        for (var, coeff) in rhs.coefficients {
36            *self.coefficients.entry(var).or_insert(0.0) += coeff;
37        }
38        self.constant += rhs.constant;
39    }
40
41    // --- Constraint builders ---
42
43    /// Create a `<=` constraint: `self <= rhs`.
44    pub fn leq(self, rhs: impl Into<Expression>) -> Constraint {
45        let mut lhs = self;
46        let mut rhs_expr = rhs.into();
47        // Normalize: move rhs to left → (lhs - rhs) <= 0
48        // Keep as: lhs_expr sense rhs_val
49        // We store as `lhs - rhs.constant` and `rhs.variables` moved to lhs negated
50        for (var, coeff) in rhs_expr.coefficients.drain() {
51            *lhs.coefficients.entry(var).or_insert(0.0) -= coeff;
52        }
53        let rhs_val = rhs_expr.constant - lhs.constant;
54        lhs.constant = 0.0;
55        Constraint {
56            lhs,
57            rhs: rhs_val,
58            sense: ConstraintSense::Le,
59        }
60    }
61
62    /// Create a `>=` constraint: `self >= rhs`.
63    pub fn geq(self, rhs: impl Into<Expression>) -> Constraint {
64        let mut lhs = self;
65        let mut rhs_expr = rhs.into();
66        for (var, coeff) in rhs_expr.coefficients.drain() {
67            *lhs.coefficients.entry(var).or_insert(0.0) -= coeff;
68        }
69        let rhs_val = rhs_expr.constant - lhs.constant;
70        lhs.constant = 0.0;
71        Constraint {
72            lhs,
73            rhs: rhs_val,
74            sense: ConstraintSense::Ge,
75        }
76    }
77
78    /// Create an `==` constraint: `self == rhs`.
79    pub fn eq_constraint(self, rhs: impl Into<Expression>) -> Constraint {
80        let mut lhs = self;
81        let mut rhs_expr = rhs.into();
82        for (var, coeff) in rhs_expr.coefficients.drain() {
83            *lhs.coefficients.entry(var).or_insert(0.0) -= coeff;
84        }
85        let rhs_val = rhs_expr.constant - lhs.constant;
86        lhs.constant = 0.0;
87        Constraint {
88            lhs,
89            rhs: rhs_val,
90            sense: ConstraintSense::Eq,
91        }
92    }
93}
94
95// --- From conversions ---
96
97impl From<Variable> for Expression {
98    fn from(var: Variable) -> Self {
99        let mut coefficients = HashMap::new();
100        coefficients.insert(var, 1.0);
101        Expression {
102            coefficients,
103            constant: 0.0,
104        }
105    }
106}
107
108impl From<f64> for Expression {
109    fn from(c: f64) -> Self {
110        Expression::from_constant(c)
111    }
112}
113
114impl From<i32> for Expression {
115    fn from(c: i32) -> Self {
116        Expression::from_constant(c as f64)
117    }
118}
119
120// --- Negation ---
121
122impl Neg for Expression {
123    type Output = Expression;
124    fn neg(mut self) -> Expression {
125        for coeff in self.coefficients.values_mut() {
126            *coeff = -*coeff;
127        }
128        self.constant = -self.constant;
129        self
130    }
131}
132
133impl Neg for Variable {
134    type Output = Expression;
135    fn neg(self) -> Expression {
136        -Expression::from(self)
137    }
138}
139
140// --- Expression + Expression ---
141
142impl Add for Expression {
143    type Output = Expression;
144    fn add(mut self, rhs: Expression) -> Expression {
145        self.merge_add(rhs);
146        self
147    }
148}
149
150// --- Expression - Expression ---
151
152impl Sub for Expression {
153    type Output = Expression;
154    fn sub(self, rhs: Expression) -> Expression {
155        self + (-rhs)
156    }
157}
158
159// --- f64 * Expression ---
160
161impl Mul<Expression> for f64 {
162    type Output = Expression;
163    fn mul(self, mut rhs: Expression) -> Expression {
164        for coeff in rhs.coefficients.values_mut() {
165            *coeff *= self;
166        }
167        rhs.constant *= self;
168        rhs
169    }
170}
171
172impl Mul<f64> for Expression {
173    type Output = Expression;
174    fn mul(self, rhs: f64) -> Expression {
175        rhs * self
176    }
177}
178
179// --- f64 * Variable → Expression ---
180
181impl Mul<Variable> for f64 {
182    type Output = Expression;
183    fn mul(self, rhs: Variable) -> Expression {
184        let mut coefficients = HashMap::new();
185        coefficients.insert(rhs, self);
186        Expression {
187            coefficients,
188            constant: 0.0,
189        }
190    }
191}
192
193impl Mul<f64> for Variable {
194    type Output = Expression;
195    fn mul(self, rhs: f64) -> Expression {
196        rhs * self
197    }
198}
199
200// --- Variable + Variable → Expression ---
201
202impl Add<Variable> for Variable {
203    type Output = Expression;
204    fn add(self, rhs: Variable) -> Expression {
205        Expression::from(self) + Expression::from(rhs)
206    }
207}
208
209impl Sub<Variable> for Variable {
210    type Output = Expression;
211    fn sub(self, rhs: Variable) -> Expression {
212        Expression::from(self) - Expression::from(rhs)
213    }
214}
215
216// --- Variable + Expression / Expression + Variable ---
217
218impl Add<Expression> for Variable {
219    type Output = Expression;
220    fn add(self, rhs: Expression) -> Expression {
221        Expression::from(self) + rhs
222    }
223}
224
225impl Add<Variable> for Expression {
226    type Output = Expression;
227    fn add(mut self, rhs: Variable) -> Expression {
228        *self.coefficients.entry(rhs).or_insert(0.0) += 1.0;
229        self
230    }
231}
232
233impl Sub<Expression> for Variable {
234    type Output = Expression;
235    fn sub(self, rhs: Expression) -> Expression {
236        Expression::from(self) - rhs
237    }
238}
239
240impl Sub<Variable> for Expression {
241    type Output = Expression;
242    fn sub(mut self, rhs: Variable) -> Expression {
243        *self.coefficients.entry(rhs).or_insert(0.0) -= 1.0;
244        self
245    }
246}
247
248// --- f64 + Expression / Expression + f64 ---
249
250impl Add<f64> for Expression {
251    type Output = Expression;
252    fn add(mut self, rhs: f64) -> Expression {
253        self.constant += rhs;
254        self
255    }
256}
257
258impl Add<Expression> for f64 {
259    type Output = Expression;
260    fn add(self, mut rhs: Expression) -> Expression {
261        rhs.constant += self;
262        rhs
263    }
264}
265
266impl Sub<f64> for Expression {
267    type Output = Expression;
268    fn sub(mut self, rhs: f64) -> Expression {
269        self.constant -= rhs;
270        self
271    }
272}
273
274impl Sub<Expression> for f64 {
275    type Output = Expression;
276    fn sub(self, rhs: Expression) -> Expression {
277        self + (-rhs)
278    }
279}
280
281// --- f64 + Variable / Variable + f64 ---
282
283impl Add<f64> for Variable {
284    type Output = Expression;
285    fn add(self, rhs: f64) -> Expression {
286        Expression::from(self) + rhs
287    }
288}
289
290impl Add<Variable> for f64 {
291    type Output = Expression;
292    fn add(self, rhs: Variable) -> Expression {
293        self + Expression::from(rhs)
294    }
295}
296
297impl Sub<f64> for Variable {
298    type Output = Expression;
299    fn sub(self, rhs: f64) -> Expression {
300        Expression::from(self) - rhs
301    }
302}
303
304impl Sub<Variable> for f64 {
305    type Output = Expression;
306    fn sub(self, rhs: Variable) -> Expression {
307        self - Expression::from(rhs)
308    }
309}