Permalink
Browse files

[Propagator][Cumulative] Test framework for cumulative and fix a out-…

…of-bound bug.
  • Loading branch information...
ptal committed Dec 11, 2016
1 parent 89a9816 commit 4853b89b27ea754a6b274f345f95dd4fc4942eb0
Showing with 130 additions and 85 deletions.
  1. +2 −8 example/src/main.rs
  2. +21 −21 example/src/robot.rs
  3. +5 −5 lang/Cargo.toml
  4. +31 −9 src/libpcp/propagation/store.rs
  5. +71 −42 src/libpcp/propagators/cumulative.rs
View
@@ -30,12 +30,6 @@ mod robot;
use nqueens::nqueens;
fn main() {
nqueens(2);
println!("");
nqueens(3);
println!("");
nqueens(10);
println!("");
robot::solve_schedule(4, robot::build_store(4, 500), true);
nqueens(30);
// robot::solve_schedule(4, robot::build_store(4, 500), true);
}
View
@@ -137,30 +137,30 @@ pub fn solve_schedule(num_robot: usize, space: FDSpace, show_trace: bool) {
#[cfg(test)]
mod tests {
use test::Bencher;
use test::Bencher;
#[test]
fn test_shedule() {
super::solve_schedule(4, super::build_store(4, 500), true);
}
#[test]
fn test_shedule() {
super::solve_schedule(4, super::build_store(4, 500), true);
}
#[bench]
fn bench_schedule_2(b: &mut Bencher) {
b.iter(|| super::solve_schedule(2, super::build_store(2, 500), false));
}
#[bench]
fn bench_schedule_2(b: &mut Bencher) {
b.iter(|| super::solve_schedule(2, super::build_store(2, 500), false));
}
#[bench]
fn bench_schedule_4(b: &mut Bencher) {
b.iter(|| super::solve_schedule(4, super::build_store(4, 500), false));
}
#[bench]
fn bench_schedule_4(b: &mut Bencher) {
b.iter(|| super::solve_schedule(4, super::build_store(4, 500), false));
}
#[bench]
fn bench_schedule_8(b: &mut Bencher) {
b.iter(|| super::solve_schedule(8, super::build_store(8, 500), false));
}
#[bench]
fn bench_schedule_8(b: &mut Bencher) {
b.iter(|| super::solve_schedule(8, super::build_store(8, 500), false));
}
#[bench]
fn bench_schedule_16(b: &mut Bencher) {
b.iter(|| super::solve_schedule(16, super::build_store(16, 500), false));
}
#[bench]
fn bench_schedule_16(b: &mut Bencher) {
b.iter(|| super::solve_schedule(16, super::build_store(16, 500), false));
}
}
View
@@ -16,11 +16,11 @@ plugin = true
[dependencies.pcp]
path = ".."
version = "^0.1.3"
version = "^0.2.0"
[dependencies]
oak = "^0.4.1"
oak_runtime = "^0.4.0"
intervallum = "^0.5.5"
gcollections = "^0.2.2"
oak = "^0.4.5"
oak_runtime = "^0.4.1"
intervallum = "^0.6.0"
gcollections = "^0.3.2"
ama = "^0.1.3"
@@ -19,7 +19,7 @@ use kernel::Trilean::*;
use propagation::Reactor;
use propagation::Scheduler;
use propagation::concept::*;
use propagation::ops::Subsumption;
use propagation::ops::*;
use variable::ops::*;
use gcollections::kind::*;
use gcollections::ops::*;
@@ -86,17 +86,15 @@ impl<VStore, Event, R, S> Store<VStore, Event, R, S> where
}
}
fn propagation_loop(&mut self, store: &mut VStore) -> Trilean {
let mut unsatisfiable = false;
fn propagation_loop(&mut self, store: &mut VStore) -> bool {
let mut consistent = true;
while let Some(p_idx) = self.scheduler.pop() {
if !self.propagate_one(p_idx, store) {
unsatisfiable = true;
consistent = false;
break;
}
}
if unsatisfiable { False }
else if self.reactor.is_empty() { True }
else { Unknown }
consistent
}
fn propagate_one(&mut self, p_idx: usize, store: &mut VStore) -> bool {
@@ -149,18 +147,42 @@ impl<VStore, Event, R, S> Subsumption<VStore> for Store<VStore, Event, R, S>
}
}
impl<VStore, Event, R, S> Consistency<VStore> for Store<VStore, Event, R, S> where
impl<VStore, Event, R, S> Propagator<VStore> for Store<VStore, Event, R, S> where
VStore: Cardinality<Size=usize> + DrainDelta<Event>,
Event: EventIndex,
R: Reactor + Cardinality<Size=usize>,
S: Scheduler
{
fn consistency(&mut self, store: &mut VStore) -> Trilean {
fn propagate(&mut self, store: &mut VStore) -> bool {
self.prepare(store);
self.propagation_loop(store)
}
}
impl<VStore, Event, R, S> PropagatorDependencies<Event> for Store<VStore, Event, R, S>
{
fn dependencies(&self) -> Vec<(usize, Event)> {
self.propagators.iter()
.map(|p| p.dependencies())
.flat_map(|deps| deps.into_iter())
.collect()
}
}
impl<VStore, Event, R, S> Consistency<VStore> for Store<VStore, Event, R, S> where
VStore: Cardinality<Size=usize> + DrainDelta<Event>,
Event: EventIndex,
R: Reactor + Cardinality<Size=usize>,
S: Scheduler
{
fn consistency(&mut self, store: &mut VStore) -> Trilean {
let consistent = self.propagate(store);
if !consistent { False }
else if self.reactor.is_empty() { True }
else { Unknown }
}
}
impl<VStore, Event, R, S> Clone for Store<VStore, Event, R, S> where
Event: EventIndex,
R: Reactor,
@@ -17,12 +17,12 @@ use propagation::*;
use propagation::events::*;
use term::ops::*;
use term::bool2int::*;
use interval::ops::{Range, Whole};
use interval::ops::{Range};
use gcollections::ops::*;
use gcollections::*;
use gcollections::IntervalKind;
use num::{Integer, PrimInt, Signed};
use std::ops::{Add, Sub};
use std::ops::{Add, Sub, Mul};
use std::marker::PhantomData;
pub struct Cumulative<V, VStore>
@@ -57,11 +57,11 @@ impl<V, Bound, VStore, Dom> Cumulative<V, VStore> where
V: StoreMonotonicUpdate<VStore>,
V: StoreRead<VStore>,
V: Clone + 'static,
Dom: Bounded<Item=Bound> + Add<Output=Dom> + Sub<Output=Dom> + Clone + Whole,
Dom: Bounded<Item=Bound> + Add<Bound, Output=Dom> + Add<Output=Dom> + Sub<Bound, Output=Dom> + Mul<Output=Dom> + Clone,
Dom: Singleton + Overlap + Intersection<Output=Dom> + Cardinality + Range,
Dom: Empty + ShrinkLeft + ShrinkRight + IntervalKind + 'static + PrimInt + Signed,
Dom: Empty + ShrinkLeft + ShrinkRight + IntervalKind + 'static,
Bound: PartialOrd,
Bound: Integer + PrimInt + 'static
Bound: Integer + PrimInt + Signed + 'static
{
// Decomposition described in `Why cumulative decomposition is not as bad as it sounds`, Schutt and al., 2009.
// forall( j in tasks ) (
@@ -80,22 +80,25 @@ impl<V, Bound, VStore, Dom> Cumulative<V, VStore> where
// bool2int(s[i] <= s[j] /\ s[j] < s[i] + d[i])
let mut conj: CStore = CStore::empty();
// s[i] <= s[j]
conj.alloc(box x_leq_y(self.start_at(i), self.start_at(j)));
conj.alloc(box x_leq_y::<_,_,Bound>(self.start_at(i), self.start_at(j)));
// s[j] < s[i] + d[i]
conj.alloc(box XLessYPlusZ::new(self.start_at(j), self.start_at(i), self.duration_at(i)));
let b2i = Bool2Int::new(conj);
// r = b2i * r[i]
let r = vstore.alloc(Dom::whole());
cstore.alloc(box XEqYMulZ::new(r.clone(), b2i, self.resource_at(i)));
let ri = self.resource_at(i);
let ri_ub = ri.read(vstore).upper();
let r = vstore.alloc(Dom::new(Bound::zero(), ri_ub));
cstore.alloc(box XEqYMulZ::new(r.clone(), b2i, ri));
resource_vars.push(r);
}
}
// sum( i in tasks where i != j )(...)
let mut sum = resource_vars.pop().expect("Need at least two tasks.");
for r in resource_vars {
let sum2 = vstore.alloc(Dom::whole());
cstore.alloc(box XEqYPlusZ::new(sum2.clone(), sum, r));
let sum2_dom = sum.read(vstore) + r.read(vstore);
let sum2 = vstore.alloc(sum2_dom);
cstore.alloc(box XEqYPlusZ::<_,_,_,Bound>::new(sum2.clone(), sum, r));
sum = sum2;
}
// c >= r[j] + sum
@@ -122,41 +125,67 @@ mod test {
use super::*;
use kernel::*;
use kernel::Trilean::*;
use propagation::events::FDEvent::*;
use interval::interval::*;
use propagators::test::*;
use variable::VStoreCopy;
use propagation::CStoreFD;
#[test]
fn cumulative_test() {
let dom0_10 = (0,10).to_interval();
let dom10_20 = (10,20).to_interval();
let dom10_11 = (10,11).to_interval();
let dom5_15 = (5,15).to_interval();
let dom1_10 = (1,10).to_interval();
let dom5_10 = (5,10).to_interval();
let dom6_10 = (6,10).to_interval();
let dom1_1 = (1,1).to_interval();
let dom2_2 = (2,2).to_interval();
culmulative_test_one(1, dom0_10, dom0_10, dom0_10,
Unknown, Unknown, vec![(0, Bound), (1, Bound), (2, Bound)], true);
culmulative_test_one(2, dom10_11, dom5_15, dom5_15,
Unknown, True, vec![(0, Assignment), (1, Assignment), (2, Assignment)], true);
culmulative_test_one(3, dom10_20, dom1_1, dom1_1,
True, True, vec![], true);
culmulative_test_one(4, dom1_1, dom1_1, dom1_1,
False, False, vec![], false);
culmulative_test_one(5, dom2_2, dom1_1, dom1_1,
False, False, vec![], false);
culmulative_test_one(6, dom6_10, dom5_10, dom1_10,
Unknown, Unknown, vec![(0, Bound), (1, Bound), (2, Bound)], true);
type VStoreFD = VStoreCopy<Interval<i32>>;
struct CumulativeTest {
starts: Vec<Interval<i32>>,
durations: Vec<Interval<i32>>,
resources: Vec<Interval<i32>>,
capacity: Interval<i32>,
}
fn culmulative_test_one(test_num: u32,
x: Interval<i32>, y: Interval<i32>, z: Interval<i32>,
before: Trilean, after: Trilean,
delta_expected: Vec<(usize, FDEvent)>, propagate_success: bool)
{
trinary_propagator_test(test_num, XGreaterYPlusZ::new, x, y, z, before, after, delta_expected, propagate_success);
impl CumulativeTest {
fn new(starts: Vec<Interval<i32>>, durations: Vec<Interval<i32>>,
resources: Vec<Interval<i32>>, capacity: Interval<i32>) -> Self
{
CumulativeTest {
starts: starts,
durations: durations,
resources: resources,
capacity: capacity
}
}
fn new_assignment(starts: Vec<i32>, durations: Vec<i32>,
resources: Vec<i32>, capacity: i32) -> Self
{
CumulativeTest::new(
starts.into_iter().map(|s| Interval::new(s, s)).collect(),
durations.into_iter().map(|d| Interval::new(d, d)).collect(),
resources.into_iter().map(|r| Interval::new(r, r)).collect(),
Interval::new(capacity, capacity)
)
}
fn instantiate(self, vstore: &mut VStoreFD, cstore: &mut CStoreFD<VStoreFD>) {
let cumulative = Cumulative::new(
self.starts.into_iter().map(|s| box vstore.alloc(s)).collect(),
self.durations.into_iter().map(|d| box vstore.alloc(d)).collect(),
self.resources.into_iter().map(|r| box vstore.alloc(r)).collect(),
box vstore.alloc(self.capacity)
);
cumulative.join(vstore, cstore);
}
fn test(self, test_num: usize, before: Trilean, after: Trilean, propagate_success: bool) {
println!("Test number {}", test_num);
let vstore = &mut VStoreFD::empty();
let cstore = &mut CStoreFD::empty();
self.instantiate(vstore, cstore);
assert_eq!(cstore.is_subsumed(vstore), before);
assert_eq!(cstore.propagate(vstore), propagate_success);
assert_eq!(cstore.is_subsumed(vstore), after);
}
}
#[test]
fn cumulative_test() {
let failed = CumulativeTest::new_assignment(
vec![0,1,4], vec![3,4,2], vec![1,2,2], 3);
failed.test(1, Unknown, False, false);
}
}

0 comments on commit 4853b89

Please sign in to comment.