Skip to content

Commit

Permalink
Merge pull request #294 from lixitrixi/improve-bounds
Browse files Browse the repository at this point in the history
Improve bounds
  • Loading branch information
ozgurakgun authored Apr 11, 2024
2 parents bbcc5b2 + 0cfbcf0 commit 6739099
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 119 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
"coverage-gutters.coverageFileNames": [
"lcov.info",
],
"workbench.iconTheme": "vs-seti",
}
2 changes: 1 addition & 1 deletion Cargo.lock

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

1 change: 0 additions & 1 deletion conjure_oxide/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ strum = "0.26.2"
versions = "6.2.0"
linkme = "0.3.22"
walkdir = "2.5.0"
itertools = "0.12.1"
regex = "1.10.3"
log = "0.4.21"
structured-logger = "1.0.3"
Expand Down
1 change: 1 addition & 0 deletions crates/conjure_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ walkdir = "2.5.0"
derivative = "2.2.0"
schemars = "0.8.16"
clap = { version = "4.5.4", features = ["derive"] }
itertools = "0.12.1"

[lints]
workspace = true
134 changes: 77 additions & 57 deletions crates/conjure_core/src/ast/domains.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use serde::{Deserialize, Serialize};
// use std::iter::Ste

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Range<A> {
pub enum Range<A>
where
A: Ord,
{
Single(A),
Bounded(A, A),
}
Expand All @@ -13,70 +17,86 @@ pub enum Domain {
}

impl Domain {
/// Returns the minimum i32 value a variable of the domain can take, if it is an i32 domain.
pub fn min_i32(&self) -> Option<i32> {
/// Return a list of all possible i32 values in the domain if it is an IntDomain.
pub fn values_i32(&self) -> Option<Vec<i32>> {
match self {
Domain::BoolDomain => Some(0),
Domain::IntDomain(ranges) => {
if ranges.is_empty() {
return None;
}
let mut min = i32::MAX;
for r in ranges {
match r {
Range::Single(i) => min = min.min(*i),
Range::Bounded(i, _) => min = min.min(*i),
}
}
Some(min)
}
Domain::IntDomain(ranges) => Some(
ranges
.iter()
.flat_map(|r| match r {
Range::Single(i) => vec![*i],
Range::Bounded(i, j) => (*i..=*j).collect(),
})
.collect(),
),
_ => None,
}
}

/// Returns the maximum i32 value a variable of the domain can take, if it is an i32 domain.
pub fn max_i32(&self) -> Option<i32> {
match self {
Domain::BoolDomain => Some(1),
Domain::IntDomain(ranges) => {
if ranges.is_empty() {
return None;
}
let mut max = i32::MIN;
for r in ranges {
match r {
Range::Single(i) => max = max.max(*i),
Range::Bounded(_, i) => max = max.max(*i),
}
}
Some(max)
/// Return an unoptimised domain that is the result of applying a binary i32 operation to two domains.
///
/// The given operator may return None if the operation is not defined for its arguments.
/// Undefined values will not be included in the resulting domain.
///
/// Returns None if the domains are not valid for i32 operations.
pub fn apply_i32(&self, op: fn(i32, i32) -> Option<i32>, other: &Domain) -> Option<Domain> {
if let (Some(vs1), Some(vs2)) = (self.values_i32(), other.values_i32()) {
// TODO: (flm8) Optimise to use smarter, less brute-force methods
let mut new_ranges = vec![];
for (v1, v2) in itertools::iproduct!(vs1, vs2) {
op(v1, v2).map(|v| new_ranges.push(Range::Single(v)));
}
return Some(Domain::IntDomain(new_ranges));
}
None
}
}

/// Returns the minimum and maximum integer values a variable of the domain can take, if it is an integer domain.
pub fn min_max_i32(&self) -> Option<(i32, i32)> {
match self {
Domain::BoolDomain => Some((0, 1)),
Domain::IntDomain(ranges) => {
if ranges.is_empty() {
return None;
}
let mut min = i32::MAX;
let mut max = i32::MIN;
for r in ranges {
match r {
Range::Single(i) => {
min = min.min(*i);
max = max.max(*i);
}
Range::Bounded(i, j) => {
min = min.min(*i);
max = max.max(*j);
}
}
}
Some((min, max))
}
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_negative_product() {
let d1 = Domain::IntDomain(vec![Range::Bounded(-2, 1)]);
let d2 = Domain::IntDomain(vec![Range::Bounded(-2, 1)]);
let res = d1.apply_i32(|a, b| Some(a * b), &d2).unwrap();

if let Domain::IntDomain(ranges) = res {
assert!(!ranges.contains(&Range::Single(-4)));
assert!(!ranges.contains(&Range::Single(-3)));
assert!(ranges.contains(&Range::Single(-2)));
assert!(ranges.contains(&Range::Single(-1)));
assert!(ranges.contains(&Range::Single(0)));
assert!(ranges.contains(&Range::Single(1)));
assert!(ranges.contains(&Range::Single(2)));
assert!(!ranges.contains(&Range::Single(3)));
assert!(ranges.contains(&Range::Single(4)));
} else {
panic!();
}
}

#[test]
fn test_negative_div() {
let d1 = Domain::IntDomain(vec![Range::Bounded(-2, 1)]);
let d2 = Domain::IntDomain(vec![Range::Bounded(-2, 1)]);
let res = d1
.apply_i32(|a, b| if b != 0 { Some(a / b) } else { None }, &d2)
.unwrap();

if let Domain::IntDomain(ranges) = res {
assert!(!ranges.contains(&Range::Single(-4)));
assert!(!ranges.contains(&Range::Single(-3)));
assert!(ranges.contains(&Range::Single(-2)));
assert!(ranges.contains(&Range::Single(-1)));
assert!(ranges.contains(&Range::Single(0)));
assert!(ranges.contains(&Range::Single(1)));
assert!(ranges.contains(&Range::Single(2)));
assert!(!ranges.contains(&Range::Single(3)));
assert!(!ranges.contains(&Range::Single(4)));
} else {
panic!();
}
}
}
Loading

0 comments on commit 6739099

Please sign in to comment.