Skip to content

Commit 4ee0e4a

Browse files
Overhaul ExtendAnti to support arbitrary prefixes
1 parent b929adf commit 4ee0e4a

File tree

1 file changed

+56
-48
lines changed

1 file changed

+56
-48
lines changed

src/treefrog.rs

+56-48
Original file line numberDiff line numberDiff line change
@@ -297,17 +297,43 @@ impl<T: Ord + Copy> Relation<T> {
297297
{
298298
extend_with::ExtendWith::from(self, key_func)
299299
}
300-
}
301300

302-
impl<Key: Ord, Val: Ord> Relation<(Key, Val)> {
303-
/// Extend with `Val` using the complement of the relation.
304-
pub fn extend_anti<'leap, Tuple: Ord, Func: Fn(&Tuple) -> Key>(
305-
&'leap self,
306-
key_func: Func,
307-
) -> extend_anti::ExtendAnti<'leap, Key, Val, Tuple, Func>
308-
where
309-
Key: 'leap,
310-
Val: 'leap,
301+
/// Extend with `<T as Split<P>>::Suffix` using the complement of the relation.
302+
///
303+
/// This leaper *removes* proposed values when
304+
/// * `key_func(src)` matches the prefix (`P`) of a tuple in this relation,
305+
/// * *AND* the proposed value matches the suffix of that same tuple.
306+
///
307+
/// It is used when a negative atom depends on a variable that is proposed by another leaper.
308+
/// For example:
309+
///
310+
/// ```prolog
311+
/// var_init_at(V, Q) :-
312+
/// var_init_at(V, P), /* leapjoin source */
313+
/// cfg_edge(P, Q), /* extend_with */
314+
/// !var_moved_at(V, Q). /* extend_anti */
315+
/// ```
316+
///
317+
/// For each source tuple in `var_init_at`, `cfg_edge` will propose some number of CFG nodes
318+
/// (`Q`). The `!var_moved_at` atom should be expressed as `extend_anti(|(v, _p)| v)`. That is,
319+
/// it extracts `V` from the source tuple (the prefix), and eliminates proposed tuples with
320+
/// that prefix whose suffix is `Q`.
321+
///
322+
/// **FIXME:** The fact that `P` determines both the prefix (in the source) *and* the suffix (the
323+
/// proposed value) is more restrictive than necessary. You could imagine a more complex program
324+
/// where the proposed value contains more information than we need for the negative atom.
325+
///
326+
/// ```prolog
327+
/// x(A, B2, C2) :-
328+
/// x(A, B1, C1), /* leapjoin source */
329+
/// t(B1, C1, B2, C2) /* Proposes `(B2, C2)` */
330+
/// !f(A, B2). /* Doesn't use `C2`! */
331+
/// ```
332+
///
333+
/// That would require a separate `val_func` (in addition to `key_func`) to extract the
334+
/// relevant part of the proposed value.
335+
pub fn extend_anti<P, F, S>(&self, key_func: F) -> extend_anti::ExtendAnti<'_, P, T, F>
336+
where F: Fn(&S) -> P
311337
{
312338
extend_anti::ExtendAnti::from(self, key_func)
313339
}
@@ -419,63 +445,45 @@ pub(crate) mod extend_anti {
419445

420446
use super::{binary_search, Leaper, Relation};
421447
use crate::join::gallop;
448+
use crate::Split;
422449

423450
/// Wraps a Relation<Tuple> as a leaper.
424-
pub struct ExtendAnti<'leap, Key, Val, Tuple, Func>
425-
where
426-
Key: Ord + 'leap,
427-
Val: Ord + 'leap,
428-
Tuple: Ord,
429-
Func: Fn(&Tuple) -> Key,
430-
{
431-
relation: &'leap Relation<(Key, Val)>,
432-
key_func: Func,
433-
old_key: Option<(Key, Range<usize>)>,
434-
phantom: ::std::marker::PhantomData<Tuple>,
451+
pub struct ExtendAnti<'a, P, T, F> {
452+
relation: &'a Relation<T>,
453+
key_func: F,
454+
old_key: Option<(P, Range<usize>)>,
435455
}
436456

437-
impl<'leap, Key, Val, Tuple, Func> ExtendAnti<'leap, Key, Val, Tuple, Func>
438-
where
439-
Key: Ord + 'leap,
440-
Val: Ord + 'leap,
441-
Tuple: Ord,
442-
Func: Fn(&Tuple) -> Key,
443-
{
457+
impl<'a, P, T, F> ExtendAnti<'a, P, T, F> {
444458
/// Constructs a ExtendAnti from a relation and key and value function.
445-
pub fn from(relation: &'leap Relation<(Key, Val)>, key_func: Func) -> Self {
446-
ExtendAnti {
447-
relation,
448-
key_func,
449-
old_key: None,
450-
phantom: ::std::marker::PhantomData,
451-
}
459+
pub fn from(relation: &'a Relation<T>, key_func: F) -> Self {
460+
ExtendAnti { relation, key_func, old_key: None }
452461
}
453462
}
454463

455-
impl<'leap, Key: Ord, Val: Ord + 'leap, Tuple: Ord, Func> Leaper<Tuple, Val>
456-
for ExtendAnti<'leap, Key, Val, Tuple, Func>
464+
impl<P, T, S, F> Leaper<S, T::Suffix> for ExtendAnti<'_, P, T, F>
457465
where
458-
Key: Ord + 'leap,
459-
Val: Ord + 'leap,
460-
Tuple: Ord,
461-
Func: Fn(&Tuple) -> Key,
466+
T: Copy + Split<P>,
467+
P: Ord,
468+
T::Suffix: Ord,
469+
F: Fn(&S) -> P,
462470
{
463-
fn count(&mut self, _prefix: &Tuple) -> usize {
471+
fn count(&mut self, _prefix: &S) -> usize {
464472
usize::max_value()
465473
}
466-
fn propose(&mut self, _prefix: &Tuple, _values: &mut Vec<Val>) {
474+
fn propose(&mut self, _prefix: &S, _values: &mut Vec<T::Suffix>) {
467475
panic!("ExtendAnti::propose(): variable apparently unbound.");
468476
}
469-
fn intersect(&mut self, prefix: &Tuple, values: &mut Vec<Val>) {
477+
fn intersect(&mut self, prefix: &S, values: &mut Vec<T::Suffix>) {
470478
let key = (self.key_func)(prefix);
471479

472480
let range = match self.old_key.as_ref() {
473481
Some((old, range)) if old == &key => range.clone(),
474482

475483
_ => {
476-
let start = binary_search(&self.relation.elements, |x| &x.0 < &key);
484+
let start = binary_search(&self.relation.elements, |x| &x.prefix() < &key);
477485
let slice1 = &self.relation[start..];
478-
let slice2 = gallop(slice1, |x| &x.0 <= &key);
486+
let slice2 = gallop(slice1, |x| &x.prefix() <= &key);
479487
let range = start..self.relation.len()-slice2.len();
480488

481489
self.old_key = Some((key, range.clone()));
@@ -487,8 +495,8 @@ pub(crate) mod extend_anti {
487495
let mut slice = &self.relation[range];
488496
if !slice.is_empty() {
489497
values.retain(|v| {
490-
slice = gallop(slice, |kv| &kv.1 < v);
491-
slice.get(0).map(|kv| &kv.1) != Some(v)
498+
slice = gallop(slice, |kv| &kv.suffix() < v);
499+
slice.get(0).map(|kv| kv.suffix()).as_ref() != Some(v)
492500
});
493501
}
494502
}

0 commit comments

Comments
 (0)