Skip to content

Commit

Permalink
feat: add set literals and set find (#687)
Browse files Browse the repository at this point in the history
Summary of changes:

added DomainSet to the Domain enum
created an expression to represent sets
added parsing for sets of literals
added parsing for finding sets of both bool and int type
added 'display' method for set domains

created test file under tests/integration/deedee/input.essence

---

* added functionality for statements like: letting a be 3

* Revert "added functionality for statements like: letting a be 3"

This reverts commit 3c7c407.

* sorry

* letting sets works now with hardcoded sets, and finding sets without any fancy parameters

* added functionality for statements like: letting a be 3

* Revert "added functionality for statements like: letting a be 3"

This reverts commit 3c7c407.

* i am confused

* merged again

* final commit before pr

* now set expression is a literal instead of a vector of expressions

* changed set attributes to match the ones in conjure, and parsing sets. Right now we can only add Atomic expressions to a set, but adding other expressions will be easy. will work on that next, this is like a POC. Also, need to work on how to display sets that contain different types of expressions, which should be easy as well

* fixed CI issues

* added expected files

* nitpicking

* really dont understand why I needed to beautify the code to this degree but hey

* i am losing my mind

* i give up

* fix submodule
  • Loading branch information
spritezs authored Mar 10, 2025
1 parent 0ebf0a2 commit ee4916d
Show file tree
Hide file tree
Showing 16 changed files with 282 additions and 34 deletions.
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions conjure_oxide/src/utils/conjure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ pub fn minion_solutions_to_json(solutions: &Vec<BTreeMap<Name, Literal>>) -> Jso
let serialized_constant = match constant {
Literal::Int(i) => JsonValue::Number((*i).into()),
Literal::Bool(b) => JsonValue::Bool(*b),
_ => panic!("Unsupported constant type"),
};
json_solution.insert(var_name.to_string(), serialized_constant);
}
Expand Down
9 changes: 9 additions & 0 deletions conjure_oxide/tests/integration/deedee/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
enable_native_impl=false
enable_rewriter_impl=false
parse_model_default=true
enable_native_parser=false
apply_rewrite_rules=false
enable_extra_validation=false
solve_with_minion=false
compare_solver_solutions=false
validate_rule_traces=false
Empty file.
1 change: 1 addition & 0 deletions conjure_oxide/tests/integration/deedee/input.essence
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
letting a be {1,2,3}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"constraints": {
"Root": [
{
"clean": false,
"etype": null
},
[]
]
},
"dominance": null,
"symbols": {
"id": 0,
"next_machine_name": 0,
"parent": null,
"table": [
[
{
"UserName": "a"
},
{
"id": 286,
"kind": {
"ValueLetting": {
"AbstractLiteral": [
{
"clean": false,
"etype": null
},
{
"Set": [
{
"Atomic": [
{
"clean": false,
"etype": null
},
{
"Literal": {
"Int": 1
}
}
]
},
{
"Atomic": [
{
"clean": false,
"etype": null
},
{
"Literal": {
"Int": 2
}
}
]
},
{
"Atomic": [
{
"clean": false,
"etype": null
},
{
"Literal": {
"Int": 3
}
}
]
}
]
}
]
}
},
"name": {
"UserName": "a"
}
}
]
]
}
}
8 changes: 3 additions & 5 deletions crates/conjure_core/src/ast/atom.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
use std::borrow::Borrow;

use crate::metadata::Metadata;

use super::{Expression, Literal, Name};
use super::{literals::AbstractLiteral, Expression, Literal, Name};
use serde::{Deserialize, Serialize};
use uniplate::derive::Uniplate;

/// An `Atom` is an indivisible expression, such as a literal or a reference.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Uniplate)]
#[uniplate()]
#[biplate(to=Name)]
#[biplate(to=Literal)]
#[biplate(to=Metadata)]
#[biplate(to=Expression)]
#[biplate(to=AbstractLiteral<Literal>,walk_into=[Literal])]
#[biplate(to=Name)]
pub enum Atom {
Literal(Literal),
Reference(Name),
Expand Down
13 changes: 12 additions & 1 deletion crates/conjure_core/src/ast/domains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,16 @@ pub enum Domain {
BoolDomain,
IntDomain(Vec<Range<i32>>),
DomainReference(Name),
DomainSet(SetAttr, Box<Domain>),
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SetAttr {
None,
Size(i32),
MinSize(i32),
MaxSize(i32),
MinMaxSize(i32, i32),
}

impl Domain {
/// Return a list of all possible i32 values in the domain if it is an IntDomain.
pub fn values_i32(&self) -> Option<Vec<i32>> {
Expand Down Expand Up @@ -80,6 +88,9 @@ impl Display for Domain {
}
}
Domain::DomainReference(name) => write!(f, "{}", name),
Domain::DomainSet(_, domain) => {
write!(f, "set of ({})", domain)
}
}
}
}
Expand Down
24 changes: 18 additions & 6 deletions crates/conjure_core/src/ast/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ use std::sync::Arc;

use serde::{Deserialize, Serialize};

use enum_compatability_macro::document_compatibility;
use uniplate::derive::Uniplate;
use uniplate::{Biplate, Uniplate as _};

use crate::ast::literals::AbstractLiteral;
use crate::ast::literals::Literal;
use crate::ast::pretty::{pretty_expressions_as_top_level, pretty_vec};
use crate::ast::symbol_table::SymbolTable;
use crate::ast::Atom;
use crate::ast::Name;
use crate::ast::ReturnType;
use crate::metadata::Metadata;
use enum_compatability_macro::document_compatibility;
use uniplate::derive::Uniplate;
use uniplate::{Biplate, Uniplate as _};

use super::{Domain, Range, SubModel, Typeable};

Expand All @@ -24,14 +24,17 @@ use super::{Domain, Range, SubModel, Typeable};
/// used to build rules and conditions for the model.
#[document_compatibility]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Uniplate)]
#[uniplate(walk_into=[Atom,SubModel])]
#[biplate(to=Literal)]
#[uniplate(walk_into=[Atom,SubModel,AbstractLiteral<Expression>])]
#[biplate(to=Metadata)]
#[biplate(to=Atom)]
#[biplate(to=Name)]
#[biplate(to=Vec<Expression>)]
#[biplate(to=SubModel)]
#[biplate(to=AbstractLiteral<Expression>)]
#[biplate(to=AbstractLiteral<Literal>,walk_into=[Atom])]
#[biplate(to=Literal,walk_into=[Atom])]
pub enum Expression {
AbstractLiteral(Metadata, AbstractLiteral<Expression>),
/// The top of the model
Root(Metadata, Vec<Expression>),

Expand Down Expand Up @@ -336,13 +339,16 @@ impl Expression {
/// Returns the possible values of the expression, recursing to leaf expressions
pub fn domain_of(&self, syms: &SymbolTable) -> Option<Domain> {
let ret = match self {
//todo
Expression::AbstractLiteral(_, _) => None,
Expression::DominanceRelation(_, _) => Some(Domain::BoolDomain),
Expression::FromSolution(_, expr) => expr.domain_of(syms),
Expression::Atomic(_, Atom::Reference(name)) => Some(syms.domain(name)?),
Expression::Atomic(_, Atom::Literal(Literal::Int(n))) => {
Some(Domain::IntDomain(vec![Range::Single(*n)]))
}
Expression::Atomic(_, Atom::Literal(Literal::Bool(_))) => Some(Domain::BoolDomain),
Expression::Atomic(_, Atom::Literal(Literal::AbstractLiteral(_))) => None,
Expression::Scope(_, _) => Some(Domain::BoolDomain),
Expression::Sum(_, exprs) => expr_vec_to_domain_i32(exprs, |x, y| Some(x + y), syms),
Expression::Product(_, exprs) => {
Expand Down Expand Up @@ -518,11 +524,13 @@ impl Expression {

pub fn return_type(&self) -> Option<ReturnType> {
match self {
Expression::AbstractLiteral(_, _) => None,
Expression::Root(_, _) => Some(ReturnType::Bool),
Expression::DominanceRelation(_, _) => Some(ReturnType::Bool),
Expression::FromSolution(_, expr) => expr.return_type(),
Expression::Atomic(_, Atom::Literal(Literal::Int(_))) => Some(ReturnType::Int),
Expression::Atomic(_, Atom::Literal(Literal::Bool(_))) => Some(ReturnType::Bool),
Expression::Atomic(_, Atom::Literal(Literal::AbstractLiteral(_))) => None,
Expression::Atomic(_, Atom::Reference(_)) => None,
Expression::Scope(_, scope) => scope.return_type(),
Expression::Abs(_, _) => Some(ReturnType::Int),
Expand Down Expand Up @@ -628,6 +636,10 @@ impl Display for Expression {
// TODO: (flm8) this will change once we implement a parser (two-way conversion)
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self {
// TODO: add display impl
Expression::AbstractLiteral(_, _) => {
write!(f, "todo")
}
Expression::Root(_, exprs) => {
write!(f, "{}", pretty_expressions_as_top_level(exprs))
}
Expand Down
76 changes: 73 additions & 3 deletions crates/conjure_core/src/ast/literals.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,82 @@
use std::fmt::{Display, Formatter};

use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::hash::Hash;
use std::hash::Hasher;
use uniplate::derive::Uniplate;
use uniplate::{Biplate, Tree, Uniplate};

use super::{Atom, Expression};

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Uniplate, Hash)]
#[uniplate()]
#[uniplate(walk_into=[AbstractLiteral<Literal>])]
#[biplate(to=Atom)]
#[biplate(to=AbstractLiteral<Literal>)]
#[biplate(to=AbstractLiteral<Expression>)]
#[biplate(to=Expression)]
/// A literal value, equivalent to constants in Conjure.
pub enum Literal {
Int(i32),
Bool(bool),
AbstractLiteral(AbstractLiteral<Literal>),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum AbstractLiteral<T: Uniplate + Biplate<AbstractLiteral<T>> + Biplate<T>> {
Set(Vec<T>),
Matrix(Vec<T>),
}

impl Hash for AbstractLiteral<Literal> {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
AbstractLiteral::Set(vec) => {
0.hash(state);
vec.hash(state);
}
AbstractLiteral::Matrix(vec) => {
1.hash(state);
vec.hash(state);
}
}
}
}

impl<T> Uniplate for AbstractLiteral<T>
where
T: Uniplate + Biplate<AbstractLiteral<T>> + Biplate<T>,
{
fn uniplate(&self) -> (Tree<Self>, Box<dyn Fn(Tree<Self>) -> Self>) {
// walking into T
match self {
AbstractLiteral::Set(vec) => {
let (f1_tree, f1_ctx) = <_ as Biplate<AbstractLiteral<T>>>::biplate(vec);
(f1_tree, Box::new(move |x| AbstractLiteral::Set(f1_ctx(x))))
}
AbstractLiteral::Matrix(vec) => {
let (f1_tree, f1_ctx) = <_ as Biplate<AbstractLiteral<T>>>::biplate(vec);
(f1_tree, Box::new(move |x| AbstractLiteral::Set(f1_ctx(x))))
}
}
}
}

impl<U, To> Biplate<To> for AbstractLiteral<U>
where
To: Uniplate,
U: Biplate<To> + Biplate<U> + Biplate<AbstractLiteral<U>>,
{
fn biplate(&self) -> (Tree<To>, Box<dyn Fn(Tree<To>) -> Self>) {
// walking into T
match self {
AbstractLiteral::Set(vec) => {
let (f1_tree, f1_ctx) = <_ as Biplate<To>>::biplate(vec);
(f1_tree, Box::new(move |x| AbstractLiteral::Set(f1_ctx(x))))
}
AbstractLiteral::Matrix(vec) => {
let (f1_tree, f1_ctx) = <_ as Biplate<To>>::biplate(vec);
(f1_tree, Box::new(move |x| AbstractLiteral::Set(f1_ctx(x))))
}
}
}
}

impl TryFrom<Literal> for i32 {
Expand Down Expand Up @@ -67,11 +135,13 @@ impl From<bool> for Literal {
}
}

// need display implementations for other types as well
impl Display for Literal {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self {
Literal::Int(i) => write!(f, "{}", i),
Literal::Bool(b) => write!(f, "{}", b),
Literal::AbstractLiteral(l) => write!(f, "{:?}", l),
}
}
}
2 changes: 2 additions & 0 deletions crates/conjure_core/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ pub use atom::Atom;
pub use declaration::*;
pub use domains::Domain;
pub use domains::Range;
pub use domains::SetAttr;
pub use expressions::Expression;
pub use literals::AbstractLiteral;
pub use literals::Literal;
pub use model::*;
pub use name::Name;
Expand Down
4 changes: 4 additions & 0 deletions crates/conjure_core/src/ast/variables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ impl Display for DecisionVariable {
Ok(())
}
Domain::DomainReference(name) => write!(f, "{}", name),
Domain::DomainSet(_, domain) => {
write!(f, "{}", domain)?;
Ok(())
}
}
}
}
Loading

0 comments on commit ee4916d

Please sign in to comment.