I am trying to model the following properties:
- Given a computation rule, and an Observable (~asset) we may raise an alert for this pair of (rule, observable).
- User can disable some rules, and we won't raise alerts for them.
I have modelled it like so:
var sig Rule {}
var sig Disabled in Rule {}
sig Observable {}
var one sig State {
var raised: Rule -> lone Observable
}
pred stutter {
State' = State
}
pred raiseAlert[r: Rule, p: Observable] {
// rule must not be disabled
r not in Disabled
(r -> p) not in State.raised
State.raised' = State.raised + (r -> p)
Rule' = Rule
}
pred muteRule[r: Rule] {
// not yet in Disabled
r not in Disabled
Disabled' = Disabled + r
// State.raised' = State.raised // this results in "No instance found" ?
State' = State
}
pred liveness {
(some r: Rule, p: Observable | raiseAlert[r, p])
or (some r: Rule | muteRule[r])
}
fact next {
always (liveness)
}
run liveness
Two questions:
- Here, in State 1 somehow we end up we less "raised" than in State 0. However, I only ever specified how to add more raised alerts, never how to remove them. How is this possible?

- What's the difference between
"State' = State"andState.raised' = State.raised? Replacing the first with the second, results inNo Instance found, and I don't understand why?
EDIT:
var sig Rule {}
var sig Disabled in Rule {}
sig Observable {}
var one sig State {
var raised: Rule -> lone Observable
}
fact {
some Rule
some Observable
}
pred stutter {
State' = State
State.raised' = State.raised
Disabled' = Disabled
Rule' = Rule
}
pred raiseAlert[r: Rule, p: Observable] {
r not in Disabled
(r -> p) not in State.raised
State.raised' = State.raised + (r -> p)
Rule' = Rule
State' = State
Disabled' = Disabled
}
pred muteRule[r: Rule] {
r not in Disabled
Disabled' = Disabled + r
State' = State
// remove those elements from raised which start with rule "r"
State.raised' = State.raised - (r <: State.raised)
}
pred liveness_only_stutter {
always ((some r: Rule, p: Observable | raiseAlert[r, p])
or (some r: Rule | muteRule[r]) or stutter)
}
pred liveness_no_instances {
always ((some r: Rule, p: Observable | raiseAlert[r, p])
or (some r: Rule | muteRule[r]))
}
pred liveness_actually_interesting {
// this one actually generates interesting instances
(some r: Rule, p: Observable | raiseAlert[r, p])
or (some r: Rule | muteRule[r])
}
run liveness_actually_interesting
After this edit:
- added a fact for some Rule, some Observable
- changed liveness from fact to predicate
- No more unexpected instances, but still, some predicates only ever stutter, and do anything useful.
- I had to remove
alwaysfrom my predicate to actually get useful stuff done In particular, they may interact by preventing the existence of any trace or by only allowing finite traces. But Alloy only considers infinite traces as instances: I have no idea what you are talking about. Feels like some magic rules coming out of secret places. Where I can read more about it? I don't think it's in the "Software Abstractions" book ...
Many comments are in order. First, I wouldn't call
livenessthe predicate representing possible traces. Usually we keeplivenessfor liveness properties we want to check, not properties we want to take as a fact.With
nextas a fact, it doesn't seem really useful torun liveness,run {}will do.Then, you have a
stutterpredicate but you don't say that this may happen as an event in thenextfact.Also notice, but maybe that was clear to you, that
Statemay take various values among a finite number of values defined by the scope. Theoneattribute only says that it has exactly one value in every instant.In
nextyou allowmuteRuleto happen, and this event doesn't constrainraised. So, as explained just before,raisedmay change arbitrarily, which is what happens in your question 1.Re question 2, if you have no instance for a
run, it's because you have over-constrained your model. So you must look again at all constraints and think about how they may interact. In particular, they may interact by preventing the existence of any trace or by only allowing finite traces. But Alloy only considers infinite traces as instances (this is one reason to systematically add astutterevent in thenextfact).Now, assessing how events interact in your model is a bit hard because, as explained above, you haven't said what happens to every variable in every event (some frame conditions are missing). In
raiseAlertfor instance, anything may happen toDisabled, toState, and toraisedon values other thanState. So for instance,raiseAlertmay increase the value ofState.raisedbut decrease it for other states at the same time. Similarly formuteRule.Please fix your model first, by stating explicitly what happens to every variable.
EDIT: Now that your model is edited, some comments.
Regarding "liveness", I was just pointing out that the name was badly chosen. But you can (and usually should) still define the predicate contents in a fact stating what are the possible traces, like this:
Second, note that in your events, you only modify
raisedonState. Which means you allowraisedto change for every other state value. I don't think this is what you mean in general. One way to say thatraisedchanges only onStateis rather to write:This way, you effectively specify what happens to every tuple in
raised.Also you forgot to say what happens to
RuleinmuteRule.Re the fact that you don't always get interesting instances, that's normal. Your system is now allowed to stutter, which is good in general, but a consequence is that you may also get "uninteresting" instances. But these are correct instances! To see other instances, you may use the
New ...buttons in the Visualizer; you could also issue specificruncommands; and you could finally (but that will need a bit more experience) add so-called "weak fairness" properties (which somehow force progress when it's possible).Finally, regarding the question on traces. I was supposing you knew about that as you're using the
varkeyword which isn't Daniel Jackson's book either. This is specific to Alloy 6: please refer to Formal Software Design with Alloy 6 for a tutorial-style on-going book.