Skip to main content

oximo_core/
constraint.rs

1use oximo_expr::{Expr, ExprId};
2use smol_str::SmolStr;
3
4/// The sense of a constraint: less-than-or-equal, greater-than-or-equal, or equality.
5#[derive(Copy, Clone, Debug, PartialEq, Eq)]
6pub enum Sense {
7    Le,
8    Ge,
9    Eq,
10}
11
12#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
13pub struct ConstraintId(pub u32);
14
15impl ConstraintId {
16    #[inline]
17    pub fn index(self) -> usize {
18        self.0 as usize
19    }
20}
21
22/// A single algebraic constraint already canonicalized as `lhs <op> rhs`,
23/// where `rhs` is a numeric constant. RHS expressions are folded into `lhs`
24/// during construction, so backends only ever see this canonical shape.
25#[derive(Clone, Debug)]
26pub struct Constraint {
27    pub name: SmolStr,
28    pub lhs: ExprId,
29    pub sense: Sense,
30    pub rhs: f64,
31    pub active: bool,
32}
33
34/// In-progress constraint produced by [`Relate::le`] / [`Relate::ge`] /
35/// [`Relate::eq`]. Pass to [`crate::Model::constraint`] to register.
36#[derive(Copy, Clone, Debug)]
37pub struct ConstraintExpr<'a> {
38    pub lhs: Expr<'a>,
39    pub sense: Sense,
40    pub rhs: f64,
41}
42
43/// Build a constraint from an expression. Lives on `Expr` itself.
44pub trait Relate<'a> {
45    fn le<R: IntoRhs<'a>>(self, rhs: R) -> ConstraintExpr<'a>;
46    fn ge<R: IntoRhs<'a>>(self, rhs: R) -> ConstraintExpr<'a>;
47    fn eq<R: IntoRhs<'a>>(self, rhs: R) -> ConstraintExpr<'a>;
48}
49
50/// What can appear on the right-hand side of a constraint. Numeric scalars
51/// stay as the canonical `rhs`. Expressions get subtracted into the LHS.
52pub trait IntoRhs<'a> {
53    fn fold_rhs(self, lhs: Expr<'a>) -> (Expr<'a>, f64);
54}
55
56impl<'a> IntoRhs<'a> for f64 {
57    fn fold_rhs(self, lhs: Expr<'a>) -> (Expr<'a>, f64) {
58        (lhs, self)
59    }
60}
61
62impl<'a> IntoRhs<'a> for i32 {
63    fn fold_rhs(self, lhs: Expr<'a>) -> (Expr<'a>, f64) {
64        (lhs, f64::from(self))
65    }
66}
67
68impl<'a> IntoRhs<'a> for Expr<'a> {
69    fn fold_rhs(self, lhs: Expr<'a>) -> (Expr<'a>, f64) {
70        (lhs - self, 0.0)
71    }
72}
73
74impl<'a> Relate<'a> for Expr<'a> {
75    fn le<R: IntoRhs<'a>>(self, rhs: R) -> ConstraintExpr<'a> {
76        let (lhs, rhs) = rhs.fold_rhs(self);
77        ConstraintExpr { lhs, sense: Sense::Le, rhs }
78    }
79
80    fn ge<R: IntoRhs<'a>>(self, rhs: R) -> ConstraintExpr<'a> {
81        let (lhs, rhs) = rhs.fold_rhs(self);
82        ConstraintExpr { lhs, sense: Sense::Ge, rhs }
83    }
84
85    fn eq<R: IntoRhs<'a>>(self, rhs: R) -> ConstraintExpr<'a> {
86        let (lhs, rhs) = rhs.fold_rhs(self);
87        ConstraintExpr { lhs, sense: Sense::Eq, rhs }
88    }
89}