conjure_core/ast/
ac_operators.rs

1use crate::{
2    ast::{Domain, Range, ReturnType},
3    matrix_expr,
4    metadata::Metadata,
5};
6
7use super::{Expression, Literal, Typeable};
8
9/// The possible kinds of associative-commutative (AC) operator.
10///
11/// AC operators take a single vector as input and are commonly used alongside comprehensions.
12#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
13pub enum ACOperatorKind {
14    And,
15    Or,
16    Product,
17    Sum,
18}
19
20impl ACOperatorKind {
21    /// Creates a new [Expression] of this AC operator kind with the given child expression.
22    ///
23    /// The child expression given should be of type matrix.
24    pub fn as_expression(&self, child_expr: Expression) -> Expression {
25        assert!(
26            matches!(child_expr.return_type(), Some(ReturnType::Matrix(_))),
27            "The child expression given to ACOperatorKind::to_expression should be of type matrix."
28        );
29        let box_expr = Box::new(child_expr);
30        match self {
31            ACOperatorKind::And => Expression::And(Metadata::new(), box_expr),
32            ACOperatorKind::Or => Expression::Or(Metadata::new(), box_expr),
33            ACOperatorKind::Product => Expression::Product(Metadata::new(), box_expr),
34            ACOperatorKind::Sum => Expression::Sum(Metadata::new(), box_expr),
35        }
36    }
37
38    /// Returns the identity element of this operation.
39    ///
40    /// # Example
41    ///
42    /// ```
43    /// use conjure_core::ast::{ac_operators::ACOperatorKind,Literal};
44    ///
45    /// let identity = ACOperatorKind::And.identity();
46    /// assert_eq!(identity,Literal::Bool(true));
47    /// ```
48    pub fn identity(&self) -> Literal {
49        match self {
50            ACOperatorKind::And => Literal::Bool(true),
51            ACOperatorKind::Or => Literal::Bool(false),
52            ACOperatorKind::Product => Literal::Int(1),
53            ACOperatorKind::Sum => Literal::Int(0),
54        }
55    }
56
57    /// Given some guard and tail expressions, constructs the skipping operator for this operation.
58    ///
59    /// The skipping operator is operator that takes some boolean guard expression b and some tail
60    /// expression x. If b is true, then it evaluates to x, otherwise it evaluates to the identity
61    /// element.
62    ///
63    /// # Usage
64    ///
65    /// This can be used to add guards to elements of AC operations. In the example model below, we
66    /// only want to multiply y*z by 2 if multiplyByTwo is true:
67    ///
68    /// ```plain
69    /// find multiplyByTwo: bool
70    /// find x: int(1..5)
71    /// find y: int(1..5)
72    /// find z: int(1..5)
73    ///
74    /// such that
75    ///  
76    /// x = product([y,z,[1,x;int(0..1)][toInt(b)]])
77    /// ```
78    ///
79    /// `[1,x;int(0..1)][toInt(b)]` is the skipping operator for product.
80    ///
81    /// This method constructs the skipping operator, substituting in the given expressions for b
82    /// and x.
83    pub fn make_skip_operation(&self, guard_expr: Expression, tail_expr: Expression) -> Expression {
84        assert!(
85            matches!(guard_expr.return_type(), Some(ReturnType::Bool)),
86            "The guard expression in a skipping operation should be type boolean."
87        );
88
89        match self {
90            ACOperatorKind::And => {
91                assert!(
92                    matches!(tail_expr.return_type(), Some(ReturnType::Bool)),
93                    "The tail expression in an and skipping operation should be type boolean."
94                );
95                let tail_expr_boxed = Box::new(tail_expr);
96                let guard_expr_boxed = Box::new(guard_expr);
97                Expression::Imply(Metadata::new(), guard_expr_boxed, tail_expr_boxed)
98            }
99            ACOperatorKind::Or => {
100                assert!(
101                    matches!(tail_expr.return_type(), Some(ReturnType::Bool)),
102                    "The tail expression in an or skipping operation should be type boolean."
103                );
104                Expression::And(
105                    Metadata::new(),
106                    Box::new(matrix_expr![guard_expr, tail_expr]),
107                )
108            }
109            ACOperatorKind::Product => {
110                assert!(
111                    matches!(tail_expr.return_type(), Some(ReturnType::Int)),
112                    "The tail expression in a product skipping operation should be type int."
113                );
114                let guard_expr_boxed = Box::new(guard_expr);
115                Expression::UnsafeIndex(
116                    Metadata::new(),
117                    Box::new(
118                        matrix_expr![Expression::Atomic(Metadata::new(),1.into()),tail_expr;Domain::Int(vec![Range::Bounded(0,1)])],
119                    ),
120                    vec![Expression::ToInt(Metadata::new(), guard_expr_boxed)],
121                )
122            }
123            ACOperatorKind::Sum => {
124                let guard_expr_boxed = Box::new(guard_expr);
125                assert!(
126                    matches!(tail_expr.return_type(), Some(ReturnType::Int)),
127                    "The tail expression in a sum skipping operation should be type int."
128                );
129                Expression::Product(
130                    Metadata::new(),
131                    Box::new(matrix_expr![
132                        Expression::ToInt(Metadata::new(), guard_expr_boxed),
133                        tail_expr
134                    ]),
135                )
136            }
137        }
138    }
139}
140
141impl TryFrom<&Expression> for ACOperatorKind {
142    type Error = ();
143    fn try_from(expr: &Expression) -> Result<Self, Self::Error> {
144        match expr {
145            Expression::And(_, _) => Ok(ACOperatorKind::And),
146            Expression::Or(_, _) => Ok(ACOperatorKind::Or),
147            Expression::Product(_, _) => Ok(ACOperatorKind::Product),
148            Expression::Sum(_, _) => Ok(ACOperatorKind::Sum),
149            _ => Err(()),
150        }
151    }
152}
153
154impl TryFrom<Expression> for ACOperatorKind {
155    type Error = ();
156
157    fn try_from(value: Expression) -> Result<Self, Self::Error> {
158        TryFrom::try_from(&value)
159    }
160}
161
162impl TryFrom<Box<Expression>> for ACOperatorKind {
163    type Error = ();
164
165    fn try_from(value: Box<Expression>) -> Result<Self, Self::Error> {
166        TryFrom::try_from(value.as_ref())
167    }
168}