conjure_core/representations/
matrix_to_atom.rs

1use std::collections::BTreeMap;
2
3use itertools::{izip, Itertools};
4
5use super::prelude::*;
6use crate::{ast::matrix, into_matrix};
7
8register_represention!(MatrixToAtom, "matrix_to_atom");
9
10#[derive(Clone, Debug)]
11pub struct MatrixToAtom {
12    src_var: Name,
13
14    // all the possible indices in this matrix, in order.
15    indices: Vec<Vec<Literal>>,
16
17    // the element domain for the matrix.
18    elem_domain: Domain,
19
20    // the index domains for the matrix.
21    index_domains: Vec<Domain>,
22}
23
24impl MatrixToAtom {
25    /// Returns the names of the representation variables, in the same order as the indices.
26    fn names(&self) -> impl Iterator<Item = Name> + '_ {
27        self.indices.iter().map(|x| self.indices_to_name(x))
28    }
29
30    /// Gets the representation variable name for a specific set of indices.
31    fn indices_to_name(&self, indices: &[Literal]) -> Name {
32        Name::RepresentedName(
33            Box::new(self.src_var.clone()),
34            self.repr_name().to_string(),
35            indices.iter().join("_"),
36        )
37    }
38
39    /// Panics if name is invalid.
40    #[allow(dead_code)]
41    fn name_to_indices(&self, name: &Name) -> Vec<Literal> {
42        let Name::RepresentedName(src_var, rule_string, suffix) = name else {
43            bug!("representation name should be Name::RepresentationOf");
44        };
45
46        assert_eq!(
47            src_var.as_ref(),
48            self.variable_name(),
49            "name should have the same source var as self"
50        );
51        assert_eq!(
52            rule_string,
53            self.repr_name(),
54            "name should have the same repr_name as self"
55        );
56
57        // FIXME: call the parser here to parse the literals properly; support more literal kinds
58        // ~niklasdewally
59        let indices = suffix.split("_").collect_vec();
60        assert_eq!(
61            indices.len(),
62            self.indices[0].len(),
63            "name should have same number of indices as self"
64        );
65
66        let parsed_indices = indices
67            .into_iter()
68            .map(|x| match x {
69                "true" => Literal::Bool(true),
70                "false" => Literal::Bool(false),
71                x if x.parse::<i32>().is_ok() => {
72                    let i: i32 = x
73                        .parse()
74                        .expect("already checked whether this parses into an int");
75                    Literal::Int(i)
76                }
77
78                x => bug!("{x} should be a string that can parse into a valid Literal"),
79            })
80            .collect_vec();
81
82        assert!(
83            self.indices.contains(&parsed_indices),
84            "indices parsed from the representation name should be valid indices for this variable"
85        );
86
87        parsed_indices
88    }
89}
90
91impl Representation for MatrixToAtom {
92    fn init(name: &Name, symtab: &SymbolTable) -> Option<Self> {
93        let domain = symtab.resolve_domain(name)?;
94
95        if !domain
96            .is_finite()
97            .expect("domain was resolved earlier, so should be ground here")
98        {
99            return None;
100        }
101
102        let Domain::DomainMatrix(elem_domain, index_domains) = domain else {
103            return None;
104        };
105
106        let indices = matrix::enumerate_indices(index_domains.clone()).collect_vec();
107
108        Some(MatrixToAtom {
109            src_var: name.clone(),
110            indices,
111            elem_domain: *elem_domain,
112            index_domains,
113        })
114    }
115
116    fn variable_name(&self) -> &Name {
117        &self.src_var
118    }
119
120    fn value_down(&self, value: Literal) -> Result<BTreeMap<Name, Literal>, ApplicationError> {
121        let Literal::AbstractLiteral(matrix) = value else {
122            return Err(RuleNotApplicable);
123        };
124
125        let AbstractLiteral::Matrix(_, ref index_domain) = matrix else {
126            return Err(RuleNotApplicable);
127        };
128
129        if index_domain != &self.index_domains[0] {
130            return Err(RuleNotApplicable);
131        }
132
133        Ok(izip!(self.names(), matrix::flatten(matrix)).collect())
134    }
135
136    fn value_up(&self, values: &BTreeMap<Name, Literal>) -> Result<Literal, ApplicationError> {
137        // TODO: this has no error checking or failures that don't panic...
138
139        let n_dims = self.index_domains.len();
140        fn inner(
141            current_index: Vec<Literal>,
142            current_dim: usize,
143            self1: &MatrixToAtom,
144            values: &BTreeMap<Name, Literal>,
145            n_dims: usize,
146        ) -> Literal {
147            if current_dim < n_dims {
148                Literal::AbstractLiteral(into_matrix![self1.index_domains[current_dim]
149                    .values()
150                    .unwrap()
151                    .into_iter()
152                    .map(|i| {
153                        let mut current_index_1 = current_index.clone();
154                        current_index_1.push(i);
155                        inner(current_index_1, current_dim + 1, self1, values, n_dims)
156                    })
157                    .collect_vec()])
158            } else {
159                values
160                    .get(&self1.indices_to_name(&current_index))
161                    .unwrap()
162                    .clone()
163            }
164        }
165
166        Ok(inner(vec![], 0, self, values, n_dims))
167    }
168
169    fn expression_down(
170        &self,
171        _: &SymbolTable,
172    ) -> Result<BTreeMap<Name, Expression>, ApplicationError> {
173        Ok(self
174            .names()
175            .map(|name| {
176                (
177                    name.clone(),
178                    Expression::Atomic(Metadata::new(), Atom::Reference(name)),
179                )
180            })
181            .collect())
182    }
183
184    fn declaration_down(&self) -> Result<Vec<Declaration>, ApplicationError> {
185        Ok(self
186            .names()
187            .map(|name| Declaration::new_var(name, self.elem_domain.clone()))
188            .collect_vec())
189    }
190
191    fn repr_name(&self) -> &str {
192        "matrix_to_atom"
193    }
194
195    fn box_clone(&self) -> Box<dyn Representation> {
196        Box::new(self.clone()) as _
197    }
198}