How to transform LTL into Automato in Promela - SPIN? - model-checking

How can I transform LTL into Automata in PROMELA? I know that with the command SPIN -f "ltl x" it is possible transform the LTL into a never claim, but I want the automata of the LTL and not the negation one. It is correct If I negate the LTL before to generate the never claim. Can anyone help me?

Spin generates the Promela code equivalent to the Buchi Automaton which matches the LTL formula, and envelops it into a never block.
From the docs:
NAME never - declaration of a temporal claim.
SYNTAX never { sequence }
DESCRIPTION A never claim can be used to define system behavior that,
for whatever reason, is of special interest. It is most commonly used
to specify behavior that should never happen. The claim is defined as
a series of propositions, or boolean expressions, on the system state
that must become true in the sequence specified for the behavior of
interest to be matched.
Therefore, if you want to have a look at the code that matches a given LTL formula, you can simply type:
~$ spin -f "LTL_FORMULA"
e.g.:
~$ spin -f "[] (q1 -> ! q0)"
never { /* [] (q1 -> ! q0) */
accept_init:
T0_init:
do
:: (((! ((q0))) || (! ((q1))))) -> goto T0_init
od;
}
An alternative way for obtaining the same code, plus a graphic representation of the Buchi Automaton, is to follow this link.
Looking at both your comments and this other question of yours, it appears that you want to check whether two LTL formulas p and g contradict each other, that is whether it is definitively the case that a model satisfying p would necessarily violate g and vice-versa.
This could be theoretically done using spin. However, this tool does not simplify the code of the Buchi Automaton and therefore it is difficult to deal with its output.
I would reccomend you to download LTL2BA (at the following link) instead. To set it up, you just need to unpack the tar.gz file and type make in the console.
Let's see a usage example:
~$ ./ltl2ba -f "([] q0) && (<> ! q0)"
never { /* ([] q0) && (<> ! q0) */
T0_init:
false;
}
Since [] q0 and <> ! q0 contradict each other, the returned Buchi automaton is empty [n.b.: by empty i mean that it has no accepting execution]. In this context, the code never { false; } is the canonical form of an empty Buchi Automaton without any accepting execution.
Disclaimer: comparing the output with never { false } to decide whether the Buchi Automaton is empty or not, might lead to spurious results if the simplification steps are unable to transform all empty automatons in the canonical form.

Related

Difficulty of implementing `case` expressions in a template-instantiation evaluator for a lazy functional language?

I'm following "Implementing functional languages: a tutorial" by SPJ, and I'm stuck on Exercise 2.18 (page 70), reproduced below. This is in the chapter about a template-instantiation evaluator for the simple lazy functional language described in the book (similar to a mini Miranda/Haskell):
Exercise 2.18. Why is it hard to introduce case expressions into the template instantiation machine?
(Hint: think about what instantiate would do with a case expression.)
The tutorial then goes on to cover an implementation of several less-general versions of destructuring structured data: an if primitive, a casePair primitive, and a caseList primitive. I haven't yet done the implementation for this section (Chapter 2 Mark 5), but I don't see why implementing these separately would be significantly easier than implementing a single case primitive.
The only plausible explanations I can offer is that the most generic case form is variadic in both number of alternatives (number of tags to match against) and arity (number of arguments to the structured data). All of the above primitives are fixed-arity and have a known number of alternatives. I don't see why this would make implementation significantly more difficult, however.
The instantiation of the case statement is straightforward:
Instantiate the scrutinee.
Instantiate the body expression of each alternative. (This may be somewhat wasteful if we substitute in unevaluated branches.) (I notice now this may be a problem, will post in an answer.)
Encapsulate the result in a new node type, NCase, where:
data Node = NAp Addr Addr
| ...
| NCase [(Int, [Name], Addr)]
Operationally, the reduction of the case statement is straightforward.
Check if the argument is evaluated.
If not, make it the new stack and push the current stack to the dump. (Similar to evaluating the argument of any primitive.)
If the argument is evaluated, then search for an alternative with a matching tag.
If no alternative with a matching tag is found, then throw an error (inexhaustive case branches).
Instantiate the body of the matching alternative with the environment augmented with the structured data arguments. (E.g., in case Pack {0, 2} 3 4 in <0> a b -> a + b, instantiate a + b with environment [a <- 3, b <- 4])
A new node type would likely have to be introduced for case (NCase) containing the list of alternatives, but that's not too dissuading.
I found a GitHub repository #bollu/timi which seems to implement a template-instantiation evaluator also following this tutorial. There is a section called "Lack of lambda and case", which attributes the lack of a generic case statement to the following reason:
Case requires us to have some notion of pattern matching / destructuring which is not present in this machine.
However, in this tutorial there is no notion of pattern-matching; we would simply be matching by tag number (an integer), so I'm not sure if this explanation is valid.
Aside, partly for myself: a very similar question was asked about special treatment for case statements in the next chapter of the tutorial (concerning G-machines rather than template-instantiation).
I think I figured it out while I was expanding on my reasoning in the question. I'll post here for posterity, but if someone has a more understandable explanation or is able to correct me I'll be happy to accept it.
The difficulty lies in the fact that the instantiate step performs all of the variable substitutions, and this happens separately from evaluation (the step function). The problem is as bollu says in the GitHub repository linked in the original question: it is not easy to destructure structured data at instantiation time. This makes it difficult to instantiate the bodies of all of the alternatives.
To illustrate this, consider the instantiation of let expressions. This works like so:
Instantiate each new binding expression.
Augment the current environment with the new bindings.
Instantiate the body with the augmented expression.
However, now consider the case of case expressions. What we want to do is:
Instantiate the scrutinee. (Which should eventually evaluate to the form Pack {m, n} a0 a1 ... an)
For each alternative (each of which has the form <m> b0 b1 ... bn -> body), augment the environment with the new bindings ([b0 <- a0, b1 <- a1, ..., bn <- an] and then instantiate the body of the alternative.)
The problem lies somewhere in between the two steps: calling instantiate on the scrutinee results in the instantiated Addr, but we don't readily have access to a1, a2, ... an to augment the environment with at instantiation time. While this might be possible if the scrutinee was a literal Pack value, if it needed further evaluation (e.g., was the evaluated result of a call to a supercombinator) then we would need to first evaluate it.
To solidify my own understanding, I'd like to answer the additional question: How do the primitives if, casePair, and caseList avoid this problem?
if trivially avoids this problem because boolean values are nullary. casePair and caseList avoid this problem by deferring the variable bindings using thunk(s); the body expressions get instantiated once the thunk is called, which is after the scrutinee is evaluated.
Possible solutions:
I'm thinking that it might be possible to get around this if we define a destructuring primitive operator on structured data objects. I.e., (Pack {m, n} a0 a1 ... an).3 would evaluate to a3.
In this case, what we could do is call instantiate scrut which would give us the address scrutAddr, and we could then augment the environment with new bindings [b0 <- (NAp .0 scrut), b1 <- (NAp .1 scrut), ..., bn <- (NAp .n scrut)].
The issue seems to lie in the fact that instantiation (substitution) and evaluation are separated. If variables were not instantiated separately from evaluation but rather added to/looked up from the environment upon binding/usage, then this would not be a problem. This is as if we placed the bodies of the case statements into thunks to be instantiated after the scrutinee is evaluated, which is similar to what casePair and caseList do.
I haven't worked through either of these alternate solutions or how much extra work they would incur.

Is it a good or a bad thing that a suite of quickcheck tests match the implementations?

I'm trying to get started with Haskell's QuickCheck, and while I am familiar with the concepts behind the testing methodology, this is the first time I am trying to put it to use on a project that goes beyond testing stuff like reverse . reverse == id and that kind of thing. I want to know if it is useful to apply it to business logic (I think it very much could be).
So a couple of existing business logic type functions that I would like to test look like the following:
shouldDiscountProduct :: User -> Product -> Bool
shouldDiscountProduct user product =
if M.isNothing (userDiscountCode user)
then False
else if (productDiscount product) then True
else False
For this function I can write a QuickCheck spec like the following:
data ShouldDiscountProductParams
= ShouldDiscountProductParams User Product
instance Show ShouldDiscountProductParams where
show (ShouldDiscountProductParams u p) =
"ShouldDiscountProductParams:\n\n" <>
"- " <> show u <> "\n\n" <>
"- " <> show p
instance Arbitrary ShouldDiscountProductParams where
arbitrary = ShouldDiscountProductParams <$> arbitrary <*> arbitrary
shouldDiscountProduct :: Spec
shouldDiscountProduct = it behavior (property verify)
where
behavior =
"when product elegible for discount\n"
<> " and user has discount code"
verify (ShouldDiscountProductParams p t) =
subject p t `shouldBe` expectation p t
subject =
SUT.shouldDiscountProduct
expectation User{..} Product{..} =
case (userDiscountCode, productDiscount) of
(Just _, Just _) -> True
_ -> False
And what I end up with is a function expectation that verifies the current implementation of shouldDiscountProduct, just more elegantly. So now I have a test, I can refactor my original function. But my natural inclination would be to change it to the implementation in expectation:
shouldDiscountProduct User{..} Product{..} =
case (userDiscountCode, productDiscount) of
(Just _, Just _) -> True
_ -> False
But this is fine right? If I want to change this function again in future I have the same function ready to verify my changes are appropriate and not inadvertently breaking something.
Or is this overkill / double bookkeeping? I suppose I have had ingrained into me from OOP testing that you should try and avoid mirroring the implementation details as much as possible, this literally couldn't be any further than that, it is the implementation!
I then think as I go through my project and add these kinds of tests, I am effectively going to be addding these tests, and then refactoring to the cleaner implementation I implement in the expectation assertion. Obviously this isn't going to be the case for more complex functions than these, but in the round I think will be the case.
What are people experiences with using property based testing for business logic-type functions? Are there any good resources out there for this kind of thing? I guess I just want to verify that I am using QC in an appropriate way, and its just my OOP past throwing doubts in my mind about this...
I'm sorry to jump in a few months later, but as this question easily pops on Google I think it needs a better answer.
Ivan's answer is about unit tests while you are talking about property tests, so let's disregard it.
Dfeuer tells you when it's acceptable to mirror the implementation, but not what to do for your use case.
It's a common mistake with Property based tests (PBT) to rewrite the implementation code at first. But this is not what PBT are for. They exist to check properties of your function. Hey, don't worry, we all do this mistake the first few times we write PBT :D
A type of property you could check here is whether your function response is consistent with its input:
if SUT.shouldDiscountProduct p t
then isJust (userDiscountCode p) && isJust (productDiscount t)
else isNothing (userDiscountCode p) || isNothing (productDiscount t)
This one is subtle in your particular use case, but pay attention, we reversed the logic. Your test checks the input, and based on this, asserts on the output. My test checks on the output, and based on this, asserts on the input. In other use cases this could be much less symmetric. Most of the code can also be refactored, I let you this exercise ;)
But you may find other types of properties! E.g. invariance properties:
SUT.shouldDiscountProduct p{userDiscountCode = Nothing} t == False
SUT.shouldDiscountProduct p{productDiscount = Nothing} t == False
See what we did here? We fixed one part of the input (e.g. the user discount code is always empty) and we assert that no matter how everything else varies, the output is invariant (always false). Same goes for product discount.
One last example: you could use an analogous property to check your old code and your new code behave exactly the same:
shouldDiscountProduct user product =
if M.isNothing (userDiscountCode user)
then False
else if (productDiscount product) then True
else False
shouldDiscountProduct' user product
| Just _ <- userDiscountCode user
, Just _ <- productDiscount product
= True
| otherwise = False
SUT.shouldDiscountProduct p t = SUT.shouldDiscountProduct' p t
Which reads as "No matter the input, the rewritten function must always return the same value as the old function". This is so cool when refactoring!
I hope this helps you grasp the idea behind Property based tests: stop worrying so much about the value returned by your function, and start wondering about some behaviors your function has.
Note, PBT are not an enemy of unit tests, they actually fit well together. You could use 1 or 2 unit tests if it makes you feel safer about actual values, then write Property test(s) to assert your function has some behaviors, no matter the input.
Basically the only times it makes sense for property checking to compare two implementations of the same function are when:
Both function are part of the API, and they should each implement a certain function. For example, we generally want liftEq (==) = (==). So we should test that liftEq for the type we're defining satisfies this property.
One implementation is obviously correct, but inefficient, while another is efficient but not obviously correct. In this case, the test suite should define the obviously correct version and check the efficient version against it.
For typical "business logic", neither of these apply. There might, however, be some special cases where they do. For example, you could have two different functions you call under different circumstances that are supposed to agree under certain conditions.
No, it's not a good thing because you're effectively comparing the results of code with results of the same code.
To resolve this chicken-and-egg problem, tests are built on these principles:
Tests feed predefined inputs and check for predefined outputs. Nothing "random". All sources of randomness are considered additional inputs and mocked or otherwise forced to produce specific values.
Sometimes, a compromise is possible: you leave a random source alone and check the output not for exact value but just for "correctness" (e.g. that it has a specific format). But then you're not testing the logic that is responsible for the parts that you don't check (though you may not need to, see below).
The only way to test a function completely is to exhastively try all possible inputs
Since this is almost always impossible, only a few "representative" ones are selected
And an assumption about the code is made that it handles all other possible inputs the same way
This is why test coverage metric is important: it will tell you when a code has changed in such a way that this assumption no longer holds
To select the optimal "representative" input, follow the function's interface.
If there are some ranges in input data that trigger different behavior, edge values are usually the most useful
Outputs are checked against the interface's promises
Sometimes, the interface doesn't promise a specific value for given inputs, variations are considered implementation details. Then you test not for a specific values but only what the interface guarantees.
Testing implementation details is only useful if other components rely on them -- then they are not really implementation details but parts of a separate, private interface.

Code generation from restricted set of input

Suppose I have a mapping (with known types) such as
1: false,
4: false,
8: true,
16: true
And I want to generate a function take input and gives the correct output. I don't care what happens for any input that is not in the above mapping, for example 3 will never be expected.
A naive solution would be to generate the function with a switch statement, for instance
f(int x) {
if x == 1 return false;
else if x == 4 return false;
else if x == 8 return true;
else if x == 16 return true;
}
I want to be able to generate code that doesn't scale in memory with the set of input.
f(int x) {
return x >= 8;
}
Does this problem have a name? What area should I research into?
You want to "guess" what code to generate for the inputs not provided.
You can't do it.
[EDIT: On further discussion, it is now clear to me that he doesn't care about such inputs. I'm leaving the answer as is, because people will assume, as I did, that he must care. Surprising advice offered at end of this answer, anyway].
Imagine you have an adversary that is going to specify a function f on all inputs, but s/he only provides you a sample, asin your example. some fixed set of inputs. You now apply an oracle, that guesses that f(9) is true. Your adversary promptly shows that her function has f(9) is actually false. Likewise if you guess f(9) is false.
The adversary can always manufacture an input/output pair that does not match what your code generator guesses. So you simply cannot get it right.
What you can do is to accept that your guesses may be wrong, and try to choose a function that has the least complexity that explains the input/output pairs you have seen so far. Your example is essentially one of these.
If you believe that "simple" functions are a better approximation of the world than complex ones, you can generate code and hope you don't encounter an adversary in nature.
Don't count on it to be reliable.
With those caveats, OP might be interested in the GNU SuperOptimizer. This finds short sequences of machine instructions that produce a provided set of input/output pairs [actually, I think you give it function that computes the answer, like OP's original function] by the "obviously" crazy idea of literally trying every instruction sequence.
The genius behind the superoptimizer is that this stunt actually works in practice for short instruction sequences.
I think it would be easy to modify it to produce generic "C" instructions (e.g. valid C actions) since I believe it uses C actions to model machine instructions anyway. You would probably have to modify your function to produce "don't care" results for inputs that don't matter, and teach GNU superoptimizer that "don't care" is a valid result. That would in fact be a useful addition to the GNU superoptimizer for its original purpose, too.

Strict vs. Non-strict Evaluation

I'm having a hard time findindg advantages/disadvantages of Eager Evaluation in comparison to non-strict evaluation and lazy eval.
I'd like to know your thoughts about that, what comparison criteria should be used as well as the advantages/disadvantages.
An additional question is why in Wikipedia
https://en.wikipedia.org/wiki/Eager_evaluation
it says: "or it may delay the evaluation of expressions that have a more immediate need."
I don't get that part... how'd eager evaluation delay evaluation of expressions that have "more immediate need"? don't understand what that's supposed to mean... any idea?
The full sentence is:
A disadvantage of eager evaluation is that it forces the evaluation of
expressions that may not be necessary at run time, or it may delay the
evaluation of expressions that have a more immediate need.
Take the following (slightly made up) example:
List<Boolean> x = empty list;
// some times later, in some other part of the program
x.add(false);
// even later, in yet anopther part of the program
x.add(is_prime(sum(prime_factors(12345678999997773511111111111873452098612576436))));
// back in main processing
if (all(x)) {
....
} else {
....
}
Where all is something like:
boolean all(List<Boolean> it) {
for (b : list) if not b then return false;
return true;
}
Here, the result of is_prime(...) is not strictly needed to compute all(x), hence its computation delays the more immediate computation. More immediate because the value all(x) is needed to decide how to continue, while - in this case - the value is_prime(...) is not needed at all at this time. It may be used later in the elsebranch, though.

What is typestate?

What does TypeState refer to in respect to language design? I saw it mentioned in some discussions regarding a new language by mozilla called Rust.
Note: Typestate was dropped from Rust, only a limited version (tracking uninitialized and moved from variables) is left. See my note at the end.
The motivation behind TypeState is that types are immutable, however some of their properties are dynamic, on a per variable basis.
The idea is therefore to create simple predicates about a type, and use the Control-Flow analysis that the compiler execute for many other reasons to statically decorate the type with those predicates.
Those predicates are not actually checked by the compiler itself, it could be too onerous, instead the compiler will simply reasons in terms of graph.
As a simple example, you create a predicate even, which returns true if a number is even.
Now, you create two functions:
halve, which only acts on even numbers
double, which take any number, and return an even number.
Note that the type number is not changed, you do not create a evennumber type and duplicate all those functions that previously acted on number. You just compose number with a predicate called even.
Now, let's build some graphs:
a: number -> halve(a) #! error: `a` is not `even`
a: number, even -> halve(a) # ok
a: number -> b = double(a) -> b: number, even
Simple, isn't it ?
Of course it gets a bit more complicated when you have several possible paths:
a: number -> a = double(a) -> a: number, even -> halve(a) #! error: `a` is not `even`
\___________________________________/
This shows that you reason in terms of sets of predicates:
when joining two paths, the new set of predicates is the intersection of the sets of predicates given by those two paths
This can be augmented by the generic rule of a function:
to call a function, the set of predicates it requires must be satisfied
after a function is called, only the set of predicates it established is satisfied (note: arguments taken by value are not affected)
And thus the building block of TypeState in Rust:
check: checks that the predicate holds, if it does not fail, otherwise adds the predicate to set of predicates
Note that since Rust requires that predicates are pure functions, it can eliminate redundant check calls if it can prove that the predicate already holds at this point.
What Typestate lack is simple: composability.
If you read the description carefully, you will note this:
after a function is called, only the set of predicates it established is satisfied (note: arguments taken by value are not affected)
This means that predicates for a types are useless in themselves, the utility comes from annotating functions. Therefore, introducing a new predicate in an existing codebase is a bore, as the existing functions need be reviewed and tweaked to cater to explain whether or not they need/preserve the invariant.
And this may lead to duplicating functions at an exponential rate when new predicates pop up: predicates are not, unfortunately, composable. The very design issue they were meant to address (proliferation of types, thus functions), does not seem to be addressed.
It's basically an extension of types, where you don't just check whether some operation is allowed in general, but in this specific context. All that at compile time.
The original paper is actually quite readable.
There's a typestate checker written for Java, and Adam Warski's explanatory page gives some useful information. I'm only just figuring this material out myself, but if you are familiar with QuickCheck for Haskell, the application of QuickCheck to monadic state seems similar: categorise the states and explain how they change when they are mutated through the interface.
Typestate is explained as:
leverage type system to encode state changes
Implemented by creating a type for each state
Use move semantics to invalidate a state
Return the next state from the previous state
Optionally drop the state(close file, connections,...)
Compile time enforcement of logic
struct Data;
struct Signed;
impl Data {
fn sign(self) -> Signed {
Signed
}
}
let data = Data;
let singed = data.sign();
data.sign() // Compile error

Resources