Compare commits
4 Commits
domains-te
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 22a9626581 | |||
| d4e0c02b53 | |||
| 2c6d14234c | |||
| 66e1659c5b |
1
doz/.gitignore
vendored
Normal file
1
doz/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
||||
7
doz/Cargo.lock
generated
Normal file
7
doz/Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "doz"
|
||||
version = "0.1.0"
|
||||
6
doz/Cargo.toml
Normal file
6
doz/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "doz"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
103
doz/src/constraint.rs
Normal file
103
doz/src/constraint.rs
Normal file
@ -0,0 +1,103 @@
|
||||
pub mod resolution;
|
||||
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
|
||||
use crate::domain::{Domain, DomainRange};
|
||||
|
||||
pub struct Indexical
|
||||
{
|
||||
pub variable: usize,
|
||||
pub dependencies: Vec<usize>,
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub propagator: Box<dyn Fn(&[DomainRange]) -> DomainRange>,
|
||||
}
|
||||
|
||||
impl Indexical
|
||||
{
|
||||
pub fn new<T>(variable: usize, dependencies: &[usize], propagator: T) -> Indexical
|
||||
where
|
||||
T: Fn(&[DomainRange]) -> DomainRange + 'static,
|
||||
{
|
||||
Indexical {
|
||||
variable,
|
||||
dependencies: dependencies.to_vec(),
|
||||
propagator: Box::new(propagator),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClpContext
|
||||
{
|
||||
pub variables: usize,
|
||||
pub domains: HashMap<usize, Domain>,
|
||||
pub used_by: HashMap<usize, Vec<usize>>,
|
||||
pub constraints: HashMap<usize, Vec<Indexical>>,
|
||||
}
|
||||
|
||||
impl ClpContext
|
||||
{
|
||||
pub fn new() -> Self
|
||||
{
|
||||
ClpContext {
|
||||
variables: 0,
|
||||
domains: HashMap::new(),
|
||||
used_by: HashMap::new(),
|
||||
constraints: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_var(&mut self) -> usize
|
||||
{
|
||||
let k = self.variables;
|
||||
self.variables += 1;
|
||||
self.domains.insert(k, Domain::from(..));
|
||||
self.used_by.insert(k, vec![]);
|
||||
k
|
||||
}
|
||||
|
||||
pub fn insert_constraint(&mut self, indexical: Indexical)
|
||||
{
|
||||
let vec = self.constraints.entry(indexical.variable).or_insert(vec![]);
|
||||
for x in indexical.dependencies.iter()
|
||||
{
|
||||
self.used_by.get_mut(x).unwrap().push(indexical.variable);
|
||||
}
|
||||
vec.push(indexical);
|
||||
}
|
||||
|
||||
pub fn prune(&mut self) -> bool
|
||||
{
|
||||
let mut queue = VecDeque::from_iter(0..self.variables);
|
||||
|
||||
while !queue.is_empty()
|
||||
{
|
||||
// Apply unary constraints
|
||||
let var = queue.pop_front().unwrap();
|
||||
let constraints = self.constraints.get(&var);
|
||||
if constraints.is_none()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let constraints = constraints.unwrap();
|
||||
|
||||
for c in constraints.iter()
|
||||
{
|
||||
if c.dependencies.is_empty()
|
||||
{
|
||||
// Is unary constraint
|
||||
let domain = self.domains.get(&var).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ClpContext
|
||||
{
|
||||
fn default() -> Self
|
||||
{
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
0
doz/src/constraint/resolution.rs
Normal file
0
doz/src/constraint/resolution.rs
Normal file
241
doz/src/domain.rs
Normal file
241
doz/src/domain.rs
Normal file
@ -0,0 +1,241 @@
|
||||
pub mod domain_ops;
|
||||
pub mod range_ops;
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::ops::Range;
|
||||
use std::ops::RangeFrom;
|
||||
use std::ops::RangeFull;
|
||||
use std::ops::RangeTo;
|
||||
|
||||
pub struct Domain
|
||||
{
|
||||
pub ranges: Vec<DomainRange>,
|
||||
}
|
||||
|
||||
/// Represents the set
|
||||
/// $$\{x \in [\text{start}; \text{end}[ | x \equiv \text{congruent} \text{mod} \text{modulo}}$$
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct DomainRange
|
||||
{
|
||||
pub start: LowerBound,
|
||||
pub end: HigherBound,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum LowerBound
|
||||
{
|
||||
Infinity,
|
||||
Bounded(i64),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum HigherBound
|
||||
{
|
||||
Infinity,
|
||||
Bounded(i64),
|
||||
}
|
||||
|
||||
pub enum UnionResult<T>
|
||||
{
|
||||
Single(T),
|
||||
Union(T, T),
|
||||
}
|
||||
|
||||
impl Domain
|
||||
{
|
||||
pub fn empty(&self) -> Domain
|
||||
{
|
||||
Domain { ranges: vec![] }
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool
|
||||
{
|
||||
self.ranges.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl DomainRange
|
||||
{
|
||||
pub fn range(from: LowerBound, to: HigherBound) -> DomainRange
|
||||
{
|
||||
DomainRange {
|
||||
start: from,
|
||||
end: to,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Range<i64>> for Domain
|
||||
{
|
||||
fn from(value: Range<i64>) -> Self
|
||||
{
|
||||
Domain {
|
||||
ranges: vec![value.into()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeFull> for Domain
|
||||
{
|
||||
fn from(value: RangeFull) -> Self
|
||||
{
|
||||
Domain {
|
||||
ranges: vec![value.into()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeTo<i64>> for Domain
|
||||
{
|
||||
fn from(value: RangeTo<i64>) -> Self
|
||||
{
|
||||
Domain {
|
||||
ranges: vec![value.into()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeFrom<i64>> for Domain
|
||||
{
|
||||
fn from(value: RangeFrom<i64>) -> Self
|
||||
{
|
||||
Domain {
|
||||
ranges: vec![value.into()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Range<i64>> for DomainRange
|
||||
{
|
||||
fn from(value: Range<i64>) -> Self
|
||||
{
|
||||
DomainRange::range(
|
||||
LowerBound::Bounded(value.start),
|
||||
HigherBound::Bounded(value.end),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeTo<i64>> for DomainRange
|
||||
{
|
||||
fn from(value: RangeTo<i64>) -> Self
|
||||
{
|
||||
DomainRange::range(LowerBound::Infinity, HigherBound::Bounded(value.end))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeFrom<i64>> for DomainRange
|
||||
{
|
||||
fn from(value: RangeFrom<i64>) -> Self
|
||||
{
|
||||
DomainRange::range(LowerBound::Bounded(value.start), HigherBound::Infinity)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeFull> for DomainRange
|
||||
{
|
||||
fn from(_: RangeFull) -> Self
|
||||
{
|
||||
DomainRange::range(LowerBound::Infinity, HigherBound::Infinity)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for LowerBound
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
|
||||
{
|
||||
match self
|
||||
{
|
||||
LowerBound::Infinity => write!(f, "-∞"),
|
||||
LowerBound::Bounded(x) => write!(f, "{}", x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for HigherBound
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
|
||||
{
|
||||
match self
|
||||
{
|
||||
HigherBound::Infinity => write!(f, "+∞"),
|
||||
HigherBound::Bounded(x) => write!(f, "{}", x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DomainRange
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
|
||||
{
|
||||
write!(f, "{}..{}", self.start, self.end)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Domain
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
|
||||
{
|
||||
if self.is_empty()
|
||||
{
|
||||
write!(f, "Ø")
|
||||
}
|
||||
else
|
||||
{
|
||||
let len = self.ranges.len();
|
||||
for (i, r) in self.ranges.iter().enumerate()
|
||||
{
|
||||
write!(f, "{}", r)?;
|
||||
if i != len - 1
|
||||
{
|
||||
write!(f, " ∪ ")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for LowerBound
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering>
|
||||
{
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for LowerBound
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering
|
||||
{
|
||||
match (self, other)
|
||||
{
|
||||
(LowerBound::Infinity, LowerBound::Infinity) => std::cmp::Ordering::Equal,
|
||||
(LowerBound::Infinity, LowerBound::Bounded(_)) => std::cmp::Ordering::Less,
|
||||
(LowerBound::Bounded(_), LowerBound::Infinity) => std::cmp::Ordering::Greater,
|
||||
(LowerBound::Bounded(a), LowerBound::Bounded(b)) => a.cmp(b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for HigherBound
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering>
|
||||
{
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for HigherBound
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering
|
||||
{
|
||||
match (self, other)
|
||||
{
|
||||
(HigherBound::Infinity, HigherBound::Infinity) => std::cmp::Ordering::Equal,
|
||||
(HigherBound::Infinity, HigherBound::Bounded(_)) => std::cmp::Ordering::Greater,
|
||||
(HigherBound::Bounded(_), HigherBound::Infinity) => std::cmp::Ordering::Less,
|
||||
(HigherBound::Bounded(a), HigherBound::Bounded(b)) => a.cmp(b),
|
||||
}
|
||||
}
|
||||
}
|
||||
90
doz/src/domain/domain_ops.rs
Normal file
90
doz/src/domain/domain_ops.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use crate::domain::{Domain, DomainRange};
|
||||
|
||||
impl Domain
|
||||
{
|
||||
pub fn intersection(&self, domain: &Domain) -> Domain
|
||||
{
|
||||
let mut i = 0;
|
||||
let mut j = 0;
|
||||
let mut stack = vec![];
|
||||
|
||||
while i < self.ranges.len() || j < domain.ranges.len()
|
||||
{
|
||||
if i < self.ranges.len() && j < domain.ranges.len()
|
||||
{
|
||||
let a = self.ranges[i];
|
||||
let b = domain.ranges[j];
|
||||
|
||||
if a.disjoint(&b)
|
||||
{
|
||||
if a.start < b.start
|
||||
{
|
||||
i += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
let inter = a.intersection(&b);
|
||||
if let Some(range) = inter
|
||||
{
|
||||
stack.push(range);
|
||||
};
|
||||
if a.end > b.end
|
||||
{
|
||||
i += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Domain { ranges: stack }
|
||||
}
|
||||
|
||||
pub fn union(&self, domain: &Domain) -> Domain
|
||||
{
|
||||
let mut stack = self
|
||||
.ranges
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(domain.ranges.iter().copied())
|
||||
.collect::<Vec<DomainRange>>();
|
||||
|
||||
stack.sort_by(|a, b| b.start.cmp(&a.start));
|
||||
let mut ranges = vec![];
|
||||
|
||||
while !stack.is_empty()
|
||||
{
|
||||
if stack.len() >= 2
|
||||
{
|
||||
let (a, b) = (stack.pop().unwrap(), stack.pop().unwrap());
|
||||
match a.union(&b)
|
||||
{
|
||||
crate::domain::UnionResult::Single(x) => stack.push(x),
|
||||
crate::domain::UnionResult::Union(x, y) =>
|
||||
{
|
||||
ranges.push(x);
|
||||
stack.push(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if stack.len() == 1
|
||||
{
|
||||
ranges.push(stack.pop().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
Domain { ranges }
|
||||
}
|
||||
}
|
||||
124
doz/src/domain/range_ops.rs
Normal file
124
doz/src/domain/range_ops.rs
Normal file
@ -0,0 +1,124 @@
|
||||
// Module for computing intersection of domains
|
||||
|
||||
use crate::domain::DomainRange;
|
||||
use crate::domain::HigherBound;
|
||||
use crate::domain::LowerBound;
|
||||
use crate::domain::UnionResult;
|
||||
|
||||
impl DomainRange
|
||||
{
|
||||
pub fn union(&self, range: &DomainRange) -> UnionResult<DomainRange>
|
||||
{
|
||||
let (a, b) = (self.min_by_start(range), self.max_by_start(range));
|
||||
match (a.end, b.start)
|
||||
{
|
||||
(HigherBound::Infinity, LowerBound::Infinity) =>
|
||||
{
|
||||
UnionResult::Single(DomainRange::from(..))
|
||||
}
|
||||
(HigherBound::Infinity, LowerBound::Bounded(_)) => UnionResult::Single(DomainRange {
|
||||
start: a.start,
|
||||
end: HigherBound::Infinity,
|
||||
}),
|
||||
(HigherBound::Bounded(_), LowerBound::Infinity) => UnionResult::Single(DomainRange {
|
||||
start: LowerBound::Infinity,
|
||||
end: b.end,
|
||||
}),
|
||||
(HigherBound::Bounded(c), LowerBound::Bounded(d)) if c < d => UnionResult::Union(a, b),
|
||||
(HigherBound::Bounded(_), LowerBound::Bounded(_)) => UnionResult::Single(DomainRange {
|
||||
start: a.start,
|
||||
end: b.end,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intersection(&self, range: &DomainRange) -> Option<DomainRange>
|
||||
{
|
||||
let (a, b) = (self.min_by_start(range), self.max_by_start(range));
|
||||
match (a.end, b.start)
|
||||
{
|
||||
(HigherBound::Infinity, LowerBound::Infinity) => Some(DomainRange::from(..)),
|
||||
(HigherBound::Infinity, LowerBound::Bounded(a)) => Some(DomainRange::from(a..)),
|
||||
(HigherBound::Bounded(a), LowerBound::Infinity) => Some(DomainRange::from(..a)),
|
||||
(HigherBound::Bounded(a), LowerBound::Bounded(b)) if a <= b => None,
|
||||
(HigherBound::Bounded(a), LowerBound::Bounded(b)) => Some(DomainRange::from(a..b)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn without(&self, range: &DomainRange) -> DomainRange
|
||||
{
|
||||
match self.intersection(range)
|
||||
{
|
||||
Some(intersecting_part) => match self.union(&intersecting_part)
|
||||
{
|
||||
UnionResult::Single(res) => res,
|
||||
UnionResult::Union(_, _) => unreachable!(),
|
||||
},
|
||||
None => *self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symetrical_difference(&self, range: &DomainRange) -> Option<UnionResult<DomainRange>>
|
||||
{
|
||||
match self.intersection(range)
|
||||
{
|
||||
Some(intersecting_part) =>
|
||||
{
|
||||
Some((self.without(&intersecting_part)).union(&range.without(&intersecting_part)))
|
||||
}
|
||||
None => Some(self.union(range)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disjoint(&self, range: &DomainRange) -> bool
|
||||
{
|
||||
self.intersection(range).is_none()
|
||||
}
|
||||
|
||||
pub fn included_in(&self, range: &DomainRange) -> bool
|
||||
{
|
||||
if let UnionResult::Single(x) = self.union(range)
|
||||
&& x == *range
|
||||
{
|
||||
true
|
||||
}
|
||||
else
|
||||
{
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min_by_start(&self, range: &DomainRange) -> DomainRange
|
||||
{
|
||||
if self.start < range.start
|
||||
{
|
||||
*self
|
||||
}
|
||||
else
|
||||
{
|
||||
*range
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_by_start(&self, range: &DomainRange) -> DomainRange
|
||||
{
|
||||
if self.start < range.start
|
||||
{
|
||||
*range
|
||||
}
|
||||
else
|
||||
{
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min_by_end(&self, range: &DomainRange) -> DomainRange
|
||||
{
|
||||
if self.end < range.end { *self } else { *range }
|
||||
}
|
||||
|
||||
pub fn max_by_end(&self, range: &DomainRange) -> DomainRange
|
||||
{
|
||||
if self.end < range.end { *range } else { *self }
|
||||
}
|
||||
}
|
||||
2
doz/src/lib.rs
Normal file
2
doz/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod constraint;
|
||||
pub mod domain;
|
||||
21
doz/src/main.rs
Normal file
21
doz/src/main.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use doz::constraint::Indexical;
|
||||
use doz::domain::Domain;
|
||||
use doz::domain::DomainRange;
|
||||
use doz::indexical;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let even = Domain {
|
||||
ranges: vec![DomainRange {
|
||||
start: doz::domain::LowerBound::Infinity,
|
||||
end: doz::domain::HigherBound::Infinity,
|
||||
}],
|
||||
};
|
||||
|
||||
//let k = Indexical::new([Box::new(|a, b, c| a), Box::new(|a, b, c| b)]);
|
||||
//
|
||||
|
||||
let k = indexical![|[a, _b]| { a }, |[_a, b]| { b }];
|
||||
|
||||
println!("{}", even);
|
||||
}
|
||||
Reference in New Issue
Block a user