-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Non-final lattice results visible to downstream rules #22
Comments
An even more simplified repro, with less variables and generalized names: use ascent::ascent_run;
fn main() {
let prog = ascent_run! {
relation a(usize,i64) = vec![(0,2),(1,2),(1,3)];
lattice b(usize,i64);
b(id,x) <-- a(id,x);
relation b2(usize,i64);
b2(id,x) <-- b(id,x);
relation c(usize,usize,i64);
c(id,id1,x2) <-- b(id,x2), b(id1,x2), if id1 < id;
relation c2(usize,usize,i64);
c2(id,id1,x2) <-- b2(id,x2), b2(id1,x2), if id1 < id;
};
println!(
"1: {:?}\n2: {:?}",
prog.c,
prog.c2,
);
} produces:
on ascent 0.5.0 |
A similar case is if the lattice and a normal relation are mutually recursive: use ascent::ascent_run;
fn main() {
let prog = ascent_run! {
relation a(i64) = vec![(0,)];
lattice b(i64);
b(i) <-- a(i);
relation c(i64);
c(i+1) <-- b(i);
b(i) <-- c(i), if *i < 10;
};
println!(
"b: {:?}\nc: {:?}",
prog.b,
prog.c,
);
} This produces:
Which is also 'wrong', in the sense that the set of tuples after convergence contains facts (such as c(3)) that are not implied by any other fact in the set of tuples after convergence. (as b(2) once existed, but was eliminated by lattice join). This is actually a paradox, similar to writing mutually recursive relations with negation: a(x) <-- !b(x);
b(x) <-- a(x) as above if b(n) is in the result, c(n+1) is, but then b(n+1) is, but then b(n) can't due to lattice invariants. I believe any relation depending on a lattice must go to a higher stratum, and only access the lattice after it was fully computed. |
You identified an issue with lattices in Datalog, which is for the computation to be well defined, the rules need to be monotonic. In your last example, the following rule is not monotonic: Statically determining if rules are monotonic is very hard, and given the open nature of Ascent (you could invoke arbitrary Rust code in your rules) I'd say impossible in Ascent. The following papers discuss this issue in some depth: https://cs.au.dk/~magnusm/papers/pldi16/paper.pdf
Unfortunately, this doesn't guarantee monotonicity, as even rules involving a single lattice could be non-monotonic. What's more, this restriction precludes some sensible programs where relations and lattices are mutually-recursively defined. That is, one may be willing to pay the price of underdetermined semantics with non-monotonic rules and still get the benefits of lattices. I'll leave this issue open for discussion for a bit longer, but I don't think there are any satisfactory solutions here that apply to Ascent. |
@s-arash Thanks for the answer. But are my first two examples non-monotonic too? Those don't involve any recursion or feeding back anything to the lattice... |
@B-Lorentz Ah I see. I just looked at your second example. It indeed looks like a bug. The problem is that this rule requires an index on the lattice column: One simple solution may be to reject programs that require indexes on lattice columns altogether. There are more complex solutions of course. But I'm not sure if they are worth the effort. |
@s-arash |
In the following program, the relation
stop2
is a copy ofstop
.And then
obstruct
is derived fromstop
the same way asobstruct2
is fromstop2
.Therefore one would expect that the two relations have the same output, but instead the following happens:
But
stop(1,2)
is not part of the final result, (even though it is implied byroll(1,6), block(2)
, because it undergoes lattice merge withstop(1,4)
implied byroll(1,6), block(4)
.Yet it appears
stop(1,2)
is still used in deriving obstruct, even though its value isn't final yet.The text was updated successfully, but these errors were encountered: