AlloyTools / org.alloytools.alloy

Alloy is a language for describing structures and a tool for exploring them. It has been used in a wide range of applications from finding holes in security mechanisms to designing telephone switching networks. This repository contains the code for the tool.
Other
712 stars 123 forks source link

Issue visualizing skolem funs with past operators #178

Closed nmacedo closed 5 months ago

nmacedo commented 2 years ago

When considering past operators, the value of expressions may change depending on which loop unrolling it is being evaluated. Instances in the visualizer only have the value of the prefix (and the looping state), and skolem functions are statically calculated and registered in the prefix. If a function has past operators, and its value changes depending on the loop unrolling, it will always show in the visualizer its value in the first unrolling.

Minimal example:

some var sig A {}
run {always A = A'}

fun x : A {
    { a:A | not before a in A }
}

Function x is equal to A in the first unrolling, but empty in the remaining, but it always appear as A in the visualizer.

PS: the evaluator works fine since the values are calculated dynamically.

alcinocunha commented 1 year ago

I think the behaviour with skolem functions should be the same as if they were declared as signatures or fields. In this case we should have the same behaviour as if the model was

some var sig A {}
run {always A = A'}

var sig x in A {}
fact { always x = { a:A | not before a in A } }

which works fine.

To implement this correctly we need to compute the maximum depth of past operator nesting in all the skolems (in this case it is 1) and unroll the trace in the generated instance that many times, so that we make enough states distinct in the prefix for the past formulas to make sense. I'm not sure how easy it is to implement this?

nmacedo commented 6 months ago

I'm having a lot of trouble fixing this. I see two options:

Screenshot 2024-03-27 at 14 28 28
nmacedo commented 6 months ago

Ok, new plan: I unroll the trace at the XML level, but register information about the original trace. So in the example above, even though the value of the skolem function changes, it would still show like this:

Screenshot 2024-03-27 at 15 25 32

So the user still sees the instance as minimal, in the sense that the trace shows only a state 0. The only tradeoff is that the XML get a bit messy: the header says it's a trace with a single instance, but it actually contains two distinct states.

nmacedo commented 6 months ago

Richer example for illustration: original trace has two states looping to the first, value of the skolem changes at the third state.

var sig A { var r : A }

run {
    always A = A' 
    always r != r' }

fun x : A {
    { a:A | not before (before a in A) }
}
Screenshot 2024-03-27 at 15 41 00 Screenshot 2024-03-27 at 15 41 16 Screenshot 2024-03-27 at 15 41 50
alcinocunha commented 6 months ago

The proposed solution looks great!