1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
use std::error::Error;
use std::fmt;
use std::mem::swap;
use crate::graph::{Instant, TimeConstraint, TimeGraph};
use crate::TimeValue;
pub type TimePropagationResult = Result<TimePropagation,TimeInconsistencyError>;
/// Result of a propagation operation inside
/// a constraint set (graph or agenda).
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
pub enum TimePropagation {
/// The propagation is done without modifying the initial data
///
/// Typically, it is the case when we attempt to add a new time
/// constraint which is always ensured by the previous ones.
Unchanged,
/// The propagation is done and modifies the previous constraint
Propagated,
}
#[derive(Clone,Copy,Debug,PartialEq,Eq)]
pub enum TimeInconsistencyError {
/// The propagation failed but the original data are restored
/// so the graph remains unchanged.
Recovered,
/// The propagation failed and the original data are corrupted
///
/// Using corrupted data could lead to unexpected behavior (basically,
/// wrong further time propagation).
/// The graph is emptied.
Fatal,
}
impl Error for TimeInconsistencyError { }
impl fmt::Display for TimeInconsistencyError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("inconsistency detected when propagating time constraints")
}
}
impl TimeGraph
{
pub fn propagate<K:TimeConstraint>(&mut self, k: K) -> TimePropagationResult
{
if k.is_empty() {
Err(TimeInconsistencyError::Recovered)
} else {
let max = k.from().max(k.to());
if self.size() <= max {
// si i ou j n'était pas dans le graphe, on n'aura rien à propager
self.resize(max + 1);
unsafe {
// SAFETY: we have just resize the graph for that
*self.lower_mut(k.from(), k.to()) = k.lower_bound();
*self.lower_mut(k.to(), k.from()) = -k.upper_bound();
}
Ok(TimePropagation::Propagated)
} else {
unsafe {
// SAFETY: we check the size of the graph just before entering here
if k.lower_bound() <= self.lower(k.from(), k.to()) {
//- la contrainte ij ne change pas
if -k.upper_bound() <= self.lower(k.to(), k.from()) {
//- la contrainte ji ne change pas non plus, c’est fini !
Ok(TimePropagation::Unchanged)
} else if self.lower(k.from(), k.to()) > k.upper_bound() {
//- la nouvelle contrainte ji est inconsistante
Err(TimeInconsistencyError::Recovered)
} else {
//- OK, on propage la contrainte ji (et c'est tout)
*self.lower_mut(k.to(), k.from()) = -k.upper_bound();
self.propagate_lower_bound(k.to(), k.from());
Ok(TimePropagation::Propagated)
}
} else {
//- la contrainte ij change
if self.lower(k.to(), k.from()) > -k.lower_bound() {
//- la nouvelle contrainte ij est inconsistante
Err(TimeInconsistencyError::Recovered)
} else {
//- OK, on peut propager la contrainte ij
*self.lower_mut(k.from(), k.to()) = k.lower_bound();
self.propagate_lower_bound(k.from(), k.to());
if self.lower(k.to(), k.from()) < -k.upper_bound() {
//- la contrainte ji. change aussi
*self.lower_mut(k.to(), k.from()) = -k.upper_bound();
self.propagate_lower_bound(k.to(), k.from());
}
Ok(TimePropagation::Propagated)
}
}
}
}
}
}
/// Merge two timegraphs
pub fn merge(&mut self, mut graph: TimeGraph) -> TimePropagationResult
{
let mut change = false;
let mut swapped = false;
if self.size() < graph.size() { swap(self, &mut graph); swapped = true; }
self.data.iter_mut()
.zip(graph.data)
.for_each(|(a,b)| if *a < b { *a = b; change = true; });
if change {
self.global_propagation()
} else if swapped {
Ok(TimePropagation::Propagated)
} else {
Ok(TimePropagation::Unchanged)
}
}
unsafe fn propagate_lower_bound(&mut self, io:Instant, jo:Instant)
{
//- propagation incrementale
//- on suppose que la table des contraintes est a jour
//- (en nombre d'instants et en propagation des contraintes) SAUF (io,jo).
//- On applique l'algorithme de propagation globale sur les noeuds
//- qui nous interesse (donc io et jo).
//- La complexite de cet algorithme est exactement en n2+n.
//- ATTENTION: si la table n'etait pas propagee avant l'ajout de la
//- contrainte (io,jo), l'algo. fera n'importe quoi
//- (en tout cas, certainement pas la propagation complete)
//- boucle autour du noeud io
// C(i,jo) <- max (C(i,jo), (C(i,io) + C(io,jo)))
{
let io_jo = self.lower(io, jo);
for i in 0..self.size() {
let val: TimeValue = self.lower(i,io) + io_jo;
let k = self.lower_mut(i, jo);
if val > *k { *k = val; }
}
}
//- boucle autour du noeud jo
//- C(j,i) <- C(j,i) & (C(j,jo) + C(jo,i))
for j in 0..self.size() {
let j_jo = self.lower(j,jo);
for i in 0..self.size() {
let val: TimeValue = j_jo + self.lower(jo,i);
let k = self.lower_mut(j, i);
if val > *k { *k = val; }
}
}
}
/// Global propagation in O(n<sup>3</sup>).
///
/// All the graph constraints are propagated.
fn global_propagation(&mut self) -> TimePropagationResult
{
for k in 0..self.size() {
for i in 0..self.size() {
for j in 0..self.size() {
let val: TimeValue = unsafe { self.lower(i,k)+self.lower(k,j) };
let x = unsafe { self.lower_mut(i,j) };
if val > *x { *x = val; }
}
if unsafe { self.lower(i,i) }.is_strictly_positive() {
self.size = 0;
return Err(TimeInconsistencyError::Fatal)
}
}
}
Ok(TimePropagation::Propagated)
}
/// Add several constraints in one shot
///
/// If this set of constraints are inconsistent with the graph,
/// there is no possible recovery and the graph is definitively corrupted
pub fn extend<I,K>(&mut self, iter:I) -> TimePropagationResult
where
K: TimeConstraint,
I: IntoIterator<Item=K>
{
let mut iter = iter.into_iter();
match iter.size_hint() {
(_, Some(0)) => {
Ok(TimePropagation::Unchanged)
}
(_, Some(1)) => {
match iter.next() {
None => Ok(TimePropagation::Unchanged),
Some(k) => self.propagate(k)
}
}
_ => {
iter.for_each(|k| {
let max = k.from().max(k.to());
if max >= self.size() { self.resize(max+1) }
let lower = unsafe { self.lower_mut(k.from(), k.to()) };
if *lower < k.lower_bound() {
*lower = k.lower_bound();
}
// SAFETY: if lower exists (checked just above), the upper does...
let upper = unsafe { self.lower_mut(k.to(), k.from()) };
if *upper < -k.upper_bound() {
*upper = -k.upper_bound();
}
});
self.global_propagation()
}
}
}
}