Skip to main content

powdr_expression/
lib.rs

1use std::{
2    iter,
3    ops::{self, Add, Mul, Neg, Sub},
4};
5
6use powdr_number::ExpressionConvertible;
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9
10pub mod display;
11pub mod visitors;
12
13#[derive(
14    Debug,
15    PartialEq,
16    Eq,
17    PartialOrd,
18    Ord,
19    Clone,
20    Serialize,
21    Deserialize,
22    JsonSchema,
23    Hash,
24    derive_more::Display,
25)]
26pub enum AlgebraicExpression<T, R> {
27    #[serde(untagged)]
28    Reference(R),
29    #[serde(untagged)]
30    Number(T),
31    #[serde(untagged, serialize_with = "serialize_binary_operation")]
32    BinaryOperation(AlgebraicBinaryOperation<T, R>),
33    #[serde(untagged, serialize_with = "serialize_unary_operation")]
34    UnaryOperation(AlgebraicUnaryOperation<T, R>),
35}
36
37#[derive(
38    Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize, JsonSchema, Hash,
39)]
40pub struct AlgebraicBinaryOperation<T, R> {
41    pub left: Box<AlgebraicExpression<T, R>>,
42    pub op: AlgebraicBinaryOperator,
43    pub right: Box<AlgebraicExpression<T, R>>,
44}
45
46#[derive(
47    Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Serialize, Deserialize, JsonSchema, Hash,
48)]
49pub enum AlgebraicBinaryOperator {
50    #[serde(rename = "+")]
51    Add,
52    #[serde(rename = "-")]
53    Sub,
54    #[serde(rename = "*")]
55    Mul,
56}
57
58#[derive(
59    Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize, JsonSchema, Hash,
60)]
61pub struct AlgebraicUnaryOperation<T, R> {
62    pub op: AlgebraicUnaryOperator,
63    pub expr: Box<AlgebraicExpression<T, R>>,
64}
65
66#[derive(
67    Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Serialize, Deserialize, JsonSchema, Hash,
68)]
69pub enum AlgebraicUnaryOperator {
70    #[serde(rename = "-")]
71    Minus,
72}
73
74impl<T, R> AlgebraicExpression<T, R> {
75    /// Returns an iterator over all (top-level) expressions in this expression.
76    /// This specifically does not implement the Children trait because otherwise it
77    /// would have a wrong implementation of ExpressionVisitable (which is implemented
78    /// generically for all types that implement Children<Expr>).
79    fn children(&self) -> Box<dyn Iterator<Item = &AlgebraicExpression<T, R>> + '_> {
80        match self {
81            AlgebraicExpression::Reference(_) | AlgebraicExpression::Number(_) => {
82                Box::new(iter::empty())
83            }
84            AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation {
85                left, right, ..
86            }) => Box::new([left.as_ref(), right.as_ref()].into_iter()),
87            AlgebraicExpression::UnaryOperation(AlgebraicUnaryOperation { expr: e, .. }) => {
88                Box::new([e.as_ref()].into_iter())
89            }
90        }
91    }
92    /// Returns an iterator over all (top-level) expressions in this expression.
93    /// This specifically does not implement the Children trait because otherwise it
94    /// would have a wrong implementation of ExpressionVisitable (which is implemented
95    /// generically for all types that implement Children<Expr>).
96    fn children_mut(&mut self) -> Box<dyn Iterator<Item = &mut AlgebraicExpression<T, R>> + '_> {
97        match self {
98            AlgebraicExpression::Reference(_) | AlgebraicExpression::Number(_) => {
99                Box::new(iter::empty())
100            }
101            AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation {
102                left, right, ..
103            }) => Box::new([left.as_mut(), right.as_mut()].into_iter()),
104            AlgebraicExpression::UnaryOperation(AlgebraicUnaryOperation { expr: e, .. }) => {
105                Box::new([e.as_mut()].into_iter())
106            }
107        }
108    }
109
110    /// Returns the degree of the expressions
111    pub fn degree(&self) -> usize {
112        match self {
113            AlgebraicExpression::Reference(..) => 1,
114            // Multiplying two expressions adds their degrees
115            AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation {
116                op: AlgebraicBinaryOperator::Mul,
117                left,
118                right,
119            }) => left.degree() + right.degree(),
120            // In all other cases, we take the maximum of the degrees of the children
121            _ => self.children().map(|e| e.degree()).max().unwrap_or(0),
122        }
123    }
124
125    pub fn new_binary(left: Self, op: AlgebraicBinaryOperator, right: Self) -> Self {
126        AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation {
127            left: Box::new(left),
128            op,
129            right: Box::new(right),
130        })
131    }
132
133    pub fn new_unary(op: AlgebraicUnaryOperator, expr: Self) -> Self {
134        AlgebraicExpression::UnaryOperation(AlgebraicUnaryOperation {
135            op,
136            expr: Box::new(expr),
137        })
138    }
139}
140
141impl<T, R> ops::Add for AlgebraicExpression<T, R> {
142    type Output = Self;
143
144    fn add(self, rhs: Self) -> Self::Output {
145        Self::new_binary(self, AlgebraicBinaryOperator::Add, rhs)
146    }
147}
148
149impl<T, R> ops::Sub for AlgebraicExpression<T, R> {
150    type Output = Self;
151
152    fn sub(self, rhs: Self) -> Self::Output {
153        Self::new_binary(self, AlgebraicBinaryOperator::Sub, rhs)
154    }
155}
156
157impl<T, R> ops::Neg for AlgebraicExpression<T, R> {
158    type Output = Self;
159
160    fn neg(self) -> Self::Output {
161        Self::new_unary(AlgebraicUnaryOperator::Minus, self)
162    }
163}
164
165impl<T, R> ops::Mul for AlgebraicExpression<T, R> {
166    type Output = Self;
167
168    fn mul(self, rhs: Self) -> Self::Output {
169        Self::new_binary(self, AlgebraicBinaryOperator::Mul, rhs)
170    }
171}
172
173impl<T, R> From<T> for AlgebraicExpression<T, R> {
174    fn from(value: T) -> Self {
175        AlgebraicExpression::Number(value)
176    }
177}
178
179impl<T, R> ExpressionConvertible<T, R> for AlgebraicExpression<T, R> {
180    fn to_expression<
181        E: Add<E, Output = E> + Sub<E, Output = E> + Mul<E, Output = E> + Neg<Output = E>,
182    >(
183        &self,
184        number_converter: &impl Fn(&T) -> E,
185        var_converter: &impl Fn(&R) -> E,
186    ) -> E {
187        match self {
188            AlgebraicExpression::Reference(r) => var_converter(r),
189            AlgebraicExpression::Number(n) => number_converter(n),
190            AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation { left, op, right }) => {
191                let left = left.to_expression(number_converter, var_converter);
192                let right = right.to_expression(number_converter, var_converter);
193
194                match op {
195                    AlgebraicBinaryOperator::Add => left + right,
196                    AlgebraicBinaryOperator::Sub => left - right,
197                    AlgebraicBinaryOperator::Mul => left * right,
198                }
199            }
200            AlgebraicExpression::UnaryOperation(AlgebraicUnaryOperation { op, expr }) => match op {
201                AlgebraicUnaryOperator::Minus => {
202                    -expr.to_expression(number_converter, var_converter)
203                }
204            },
205        }
206    }
207}
208
209fn serialize_unary_operation<S, T, R>(
210    un_op: &AlgebraicUnaryOperation<T, R>,
211    serializer: S,
212) -> Result<S::Ok, S::Error>
213where
214    S: serde::Serializer,
215    T: Serialize,
216    R: Serialize,
217{
218    (&un_op.op, un_op.expr.as_ref()).serialize(serializer)
219}
220
221fn serialize_binary_operation<S, T, R>(
222    bin_op: &AlgebraicBinaryOperation<T, R>,
223    serializer: S,
224) -> Result<S::Ok, S::Error>
225where
226    S: serde::Serializer,
227    T: Serialize,
228    R: Serialize,
229{
230    (bin_op.left.as_ref(), &bin_op.op, bin_op.right.as_ref()).serialize(serializer)
231}
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236
237    #[test]
238    fn test_serde() {
239        let x: AlgebraicExpression<u32, &'static str> = AlgebraicExpression::from(5)
240            * AlgebraicExpression::Reference("x")
241            - AlgebraicExpression::from(3);
242        let serialized = serde_json::to_string(&x).unwrap();
243        assert_eq!(serialized, r#"[[5,"*","x"],"-",3]"#);
244        let deserialized = serde_json::from_str(&serialized).unwrap();
245        assert_eq!(x, deserialized);
246    }
247}