44
55Highly automated proof techniques are a necessary step for the widespread
66adoption of formal methods in the software industry. Moreover, it could provide
7- a partial answer to one of its main issue which is scalability.
7+ a partial answer to one of its main issues, which is scalability.
88
99* copilot-theorem* is a Copilot library aimed at checking automatically some safety
1010properties on Copilot programs. It includes:
@@ -17,9 +17,9 @@ properties on Copilot programs. It includes:
1717 proving basic k-inductive properties and for pedagogical purposes.
1818
1919* A prover producing native inputs for the * Kind2* model checker, developed at
20- University of Iowa. The latter uses both the * k-induction algorithm* extended
21- with * path compression* and * structural abstraction* [ 2] and the ** IC3
22- algorithm** with counterexample generalization based on * approximate
20+ the University of Iowa. The latter uses both the * k-induction algorithm*
21+ extended with * path compression* and * structural abstraction* [ 2] and the
22+ ** IC3 algorithm** with counterexample generalization based on * approximate
2323 quantifier elimination* [ 3] .
2424
2525## A Tutorial
@@ -39,12 +39,12 @@ README.
3939### First steps
4040
4141* copilot-theorem* is aimed at checking ** safety properties** on Copilot programs.
42- Intuitively, a safety property is a property which express the idea that
42+ Intuitively, a safety property is a property that expresses the idea that
4343* nothing bad can happen* . In particular, any invalid safety property can be
4444disproved by a finite execution trace of the program called a
4545** counterexample** . Safety properties are often opposed to ** liveness**
4646properties, which express the idea that * something good will eventually
47- happen* . The latters are out of the scope of * copilot-theorem* .
47+ happen* . The latter is out of the scope of * copilot-theorem* .
4848
4949Safety properties are simply expressed with standard boolean streams. In
5050addition to triggers and observers declarations, it is possible to bind a
@@ -70,7 +70,7 @@ This function takes three arguments:
7070* The name of the proposition we want to check.
7171* A strategy to prove the proposition.
7272
73- In this case the proposition is simple enough so that we can check it directly
73+ In this case, the proposition is simple enough so that we can check it directly
7474by k-induction using ` kind2Prover ` . Therefore we can just write:
7575
7676``` haskell
@@ -79,14 +79,15 @@ main = do
7979 prove spec' " gt0" (tell [Check $ kind2Prover def])
8080```
8181
82- where ` kind2Prover def ` stands for the * Kind2 prover* with default
82+ where ` kind2Prover def ` stands for the * Kind2 prover* with the default
8383configuration.
8484
8585#### The Kind2 prover
8686
87- The * Kind2* prover uses the model checker with the same name, from Iowa
88- university. It translates the Copilot specification into a * modular transition
89- system* (the Kind2 native format) and then calls the ` kind2 ` executable.
87+ The * Kind2* prover uses the model checker with the same name, from the
88+ University of Iowa. It translates the Copilot specification into a * modular
89+ transition system* (the Kind2 native format) and then calls the ` kind2 `
90+ executable.
9091
9192It is provided by the ` Copilot.Theorem.Kind2 ` module, which exports a `kind2Prover
9293:: Options -> Prover` where the ` Options` type is defined as
@@ -117,7 +118,7 @@ Some examples are in the *examples* folder, including:
117118### An introduction to SMT-based model checking
118119
119120An introduction to the model- checking techniques used by * copilot- theorem* can be
120- found in the `doc` folder of this repository. It consists in a self sufficient
121+ found in the `doc` folder of this repository. It consists of a self- sufficient
121122set of slides. You can find some additional readings in the * References *
122123section.
123124
@@ -216,16 +217,16 @@ process* [7] and so we have no way to keep them.
216217#### Types
217218
218219In these three formats, GADTs are used to statically ensure a part of the
219- type-corectness of the specification, in the same spirit it is done in the
220- other Copilot libraries. * copilot-theorem* handles only three types which are
221- ` Integer ` , ` Real ` and ` Bool ` and which are handled by the SMTLib standard .
220+ type-correctness of the specification, in the same spirit as in
221+ other Copilot libraries. * copilot-theorem* works with only three types,
222+ ` Integer ` , ` Real ` and ` Bool ` , all of which SMT-Lib can handle .
222223* copilot-theorem* works with * pure* reals and integers. Thus, it is unsafe in the
223224sense it ignores integer overflow problems and the loss of precision due to
224225floating point arithmetic.
225226
226227#### The Kind2 prover
227228
228- The * Kind2 prover* first translates the copilot specification into a * modular
229+ The * Kind2 prover* first translates the Copilot specification into a * modular
229230transition system* . Then, a chain of transformations is applied to this system
230231(for instance, in order to remove dependency cycles among nodes). After this,
231232the system is translated into the * Kind2 native format* and the ` kind2 `
@@ -234,7 +235,7 @@ this process.
234235
235236##### Modular transition systems
236237
237- Let's look at the definition of a * modular transition systems * , in the
238+ Let's look at the definition of a * modular transition system * , in the
238239` TransSys.Spec ` module:
239240
240241``` haskell
@@ -295,10 +296,10 @@ types of variables:
295296
296297##### The translation process
297298
298- First , a copilot specification is translated into a modular transition system.
299+ First , a Copilot specification is translated into a modular transition system.
299300This process is defined in the `TransSys. Translate ` module. Each stream is
300- associated to a node. The most significant task of this translation process is
301- to * flatten* the copilot specification so the value of all streams at time * n*
301+ associated with a node. The most significant task of this translation process is
302+ to * flatten* the Copilot specification so the value of all streams at time * n*
302303only depends on the values of all the streams at time * n - 1 * , which is not the
303304case in the `Fib ` example shown earlier. This is done by a simple program
304305transformation which turns this:
@@ -337,13 +338,13 @@ consistent. However, it can't be directly translated into the *Kind2 native
337338file format* . Indeed, it is natural to bind each node to a predicate but the
338339Kind2 file format requires that each predicate only uses previously defined
339340predicates. However, some nodes in our transition system could be mutually
340- recursive. Therefore, the goal of the ` removeCycles :: Spec -> Spec ` function
341- defined in ` TransSys.Transform ` is to remove such dependency cycles.
341+ recursive. Therefore, the goal of the ` removeCycles :: Spec -> Spec ` function,
342+ defined in ` TransSys.Transform ` , is to remove such dependency cycles.
342343
343- This function relies on the ` mergeNodes :: [NodeId] -> Spec -> Spec ` function
344- which signature is self-explicit. The latter solves name conflicts by using the
345- ` Misc.Renaming ` monad. Some code complexity has been added so the variable
346- names remains as clear as possible after merging two nodes.
344+ This function relies on the ` mergeNodes :: [NodeId] -> Spec -> Spec ` function,
345+ whose signature is self-explicit. The latter solves name conflicts by using the
346+ ` Misc.Renaming ` monad. Some code complexity has been added so variable names
347+ remain as clear as possible after merging two nodes.
347348
348349The function ` removeCycles ` computes the strongly connected components of the
349350dependency graph and merge each one into a single node. The complexity of this
@@ -354,7 +355,7 @@ merged in most practical cases.
354355After the cycles have been removed, it is useful to apply another
355356transformation which makes the translation from ` TransSys.Spec ` to ` Kind2.AST `
356357easier. This transformation is implemented in the ` complete ` function. In a
357- nutshell, it transforms a system such that
358+ nutshell, it transforms a system such that:
358359
359360* If a node depends on another, it imports * all* its variables.
360361* The dependency graph is transitive, that is if * A* depends of * B* which
@@ -374,25 +375,25 @@ inline spec = mergeNodes [nodeId n | n <- specNodes spec] spec
374375
375376which discards all the structure of a * modular transition system* and turns it
376377into a * non-modular transition system* with only one node. In fact, when
377- translating a copilot specification to a kind2 file, two styles are available:
378+ translating a Copilot specification to a Kind2 file, two styles are available:
378379the ` Kind2.toKind2 ` function takes a ` Style ` argument which can take the value
379380` Inlined ` or ` Modular ` . The only difference is that in the first case, a call
380381to ` removeCycles ` is replaced by a call to ` inline ` .
381382
382383### Limitations of copilot-theorem
383384
384385Now, we will discuss some limitations of the * copilot-theorem* tool. These
385- limitations are organized in two categories: the limitations related to the
386- Copilot language itself and its implementation, and the limitations related to
387- the model-checking techniques we are using.
386+ limitations are split into two categories: limitations related to the Copilot
387+ language itself and its implementation, and limitations related to the
388+ model-checking techniques we are using.
388389
389390#### Limitations related to Copilot implementation
390391
391- The reification process used to build the ` Core.Spec ` object looses many
392- informations about the structure of the original Copilot program. In fact, a
393- stream is kept in the reified program only if it is recursively defined.
394- Otherwise, all its occurences will be inlined. Moreover, let's look at the
395- ` intCounter ` function defined in the example ` Grey.hs ` :
392+ The reification process used to build the ` Core.Spec ` object loses information
393+ about the structure of the original Copilot program. In fact, a stream is kept
394+ in the reified program only if it is recursively defined. Otherwise, all its
395+ occurrences will be inlined. Moreover, let's look at the ` intCounter ` function
396+ defined in the example ` Grey.hs ` :
396397
397398``` haskell
398399intCounter :: Stream Bool -> Stream Word64
@@ -413,8 +414,8 @@ There are many problems with this:
413414* It makes the inputs given to the SMT solvers larger and repetitive.
414415
415416We can't rewrite the Copilot reification process in order to avoid these
416- inconvenients as these informations are lost by GHC itself before it occurs.
417- The only solution we can see would be to use * Template Haskell* to generate
417+ inconveniences as this information is lost by GHC itself before it occurs. The
418+ only solution we can see would be to use * Template Haskell* to generate
418419automatically some structural annotations, which might not be worth the dirt
419420introduced.
420421
@@ -423,7 +424,7 @@ introduced.
423424##### Limitations of the IC3 algorithm
424425
425426The IC3 algorithm was shown to be a very powerful tool for hardware
426- certification. However, the problems encountered when verifying softwares are
427+ certification. However, the problems encountered when verifying software are
427428much more complex. For now, very few non-inductive properties can be proved by
428429* Kind2* when basic integer arithmetic is involved.
429430
@@ -433,32 +434,32 @@ the inductiveness* (CTI) for a property, these techniques are used to find a
433434lemma discarding it which is general enough so that all CTIs can be discarded
434435in a finite number of steps.
435436
436- The lemmas found by the current version fo * Kind2* are often too weak. Some
437+ The lemmas found by the current version of * Kind2* are often too weak. Some
437438suggestions to enhance this are presented in [ 1] . We hope some progress will be
438- made in this area in a near future.
439+ made in this area in the near future.
439440
440- A workaround to this problem would be to write kind of an interactive mode
441- where the user is invited to provide some additional lemmas when automatic
442- techniques fail. Another solution would be to make the properties being checked
441+ A workaround to this problem would be to write an interactive mode where the
442+ user is invited to provide some additional lemmas when automatic techniques
443+ fail. Another solution would be to make the properties being checked
443444quasi-inductive by hand. In this case, * copilot-theorem* is still a useful tool
444445(especially for finding bugs) but the verification of a program can be long and
445- requires a high level of technicity .
446+ requires a high level of technical knowledge .
446447
447448##### Limitations related to the SMT solvers
448449
449- The use of SMT solvers introduces two kind of limitations:
450+ The use of SMT solvers introduces two kinds of limitations:
450451
4514521 . We are limited by the computing power needed by the SMT solvers
4524532 . SMT solvers can't handle quantifiers efficiently
453454
454455Let's consider the first point. SMT solving is costly and its performances are
455456sometimes unpredictable. For instance, when running the ` SerialBoyerMoore `
456- example with the * k-induction prover* , Yices2 does not terminate. However, the * z3 *
457+ example with the * k-induction prover* , Yices2 does not terminate. However, the * Z3 *
457458SMT solver used by * Kind2* solves the problem instantaneously. Note that this
458459performance gap is not due to the use of the IC3 algorithm because the property
459460to check is inductive. It could be related to the fact the SMT problem produced
460461by the * k-induction prover* uses uninterpreted functions for encoding streams instead
461- of simple integer variables, which is the case when the copilot program is
462+ of simple integer variables, which is the case when the Copilot program is
462463translated into a transition system. However, this wouldn't explain why the
463464* k-induction prover* still terminates instantaneously on the ` BoyerMoore ` example,
464465which seems not simpler by far.
@@ -472,10 +473,10 @@ property. This fact could be used to enhance the proof scheme system (see the
472473* Future work* section). However, this trick is not always possible. For
473474instance, in the ` SerialBoyerMoore ` example, the property being checked should
474475be quantified over all integer constants. Here, we can't just introduce an
475- arbitrary constant stream because it is the quantified property which is
476+ arbitrary constant stream because it is the quantified property that is
476477inductive and not the property specialized for a given constant stream. That's
477- why we have no other solution than replacing universal quantification by
478- * bounded* universal quantification by assuming all the elements of the input
478+ why we have no other solution than replacing universal quantification with
479+ * bounded* universal quantification, assuming all the elements of the input
479480stream are in the finite list ` allowed ` and using the function ` forAllCst ` :
480481
481482``` haskell
@@ -487,13 +488,13 @@ forAllCst l f = conj $ map (f . constant) l
487488```
488489
489490However, this solution isn't completely satisfying because the size of the
490- property generated is proportionnal to the cardinal of ` allowed ` .
491+ property generated is proportional to the length of ` allowed ` .
491492
492493#### Some scalability issues
493494
494- A standard way to prove large programs is to rely on its logical structure by
495- writing a specification for each of its functions. This very natural approach
496- is hard to follow in our case because of
495+ A standard way to prove large programs is to rely on their logical structure,
496+ by writing a specification for each of their functions. This very natural
497+ approach is hard to follow in our case due to:
497498
498499* The difficulty to deal with universal quantification.
499500* The lack of * true* functions in Copilot: the latter offers metaprogramming
@@ -503,7 +504,7 @@ is hard to follow in our case because of
503504
504505Once again, * copilot-theorem* is still a very useful tool, especially for
505506debugging purposes. However, we don't think it is adapted to write and check a
506- complete specification for large scale programs.
507+ complete specification for large- scale programs.
507508
508509## Future work
509510
@@ -512,19 +513,19 @@ complete specification for large scale programs.
512513These features are not currently provided due to the lack of important features
513514in the Kind2 SMT solver.
514515
515- #### Counterexamples displaying
516+ #### Displaying counterexamples
516517
517518Counterexamples are not displayed with the Kind2 prover because Kind2 doesn't
518519support XML output of counterexamples. If the last feature is provided, it
519- should be easy to implement counterexamples displaying in * copilot-theorem* . For
520- this, we recommend to keep some informations about * observers* in
520+ should be easy to implement displaying of counterexamples in * copilot-theorem* .
521+ For this, we recommend keeping some information about * observers* in
521522` TransSys.Spec ` and to add one variable per observer in the Kind2 output file.
522523
523524#### Bad handling of non-linear operators and external functions
524525
525526Non-linear Copilot operators and external functions are poorly handled because
526- of the lack of support of uninterpreted functions in the Kind2 native format. A
527- good way to handle these would be to use uninterpreted functions. With this
527+ of the lack of support for uninterpreted functions in the Kind2 native format.
528+ A good way to handle these would be to use uninterpreted functions. With this
528529solution, properties like
529530``` haskell
5305312 * sin x + 1 <= 3
@@ -533,7 +534,7 @@ with `x` any stream can't be proven but at least the following can be proved
533534``` haskell
534535let y = x in sin x == sin y
535536```
536- Currently, the * Kind2 prover* fail with the last example, as the results of
537+ Currently, the * Kind2 prover* fails with the last example, as the results of
537538unknown functions are turned into fresh unconstrained variables.
538539
539540### Simple extensions
@@ -542,7 +543,7 @@ The following extensions would be really simple to implement given the current
542543architecture of Kind2.
543544
544545+ If inductive proving of a property fails, giving the user a concrete CTI
545- (* Counterexample To The Inductiveness* , see the [ 1] ).
546+ (* Counterexample To The Inductiveness* , see [ 1] ).
546547
547548+ Use Template Haskell to declare automatically some observers with the same
548549 names used in the original program.
@@ -559,29 +560,29 @@ architecture of Kind2.
559560
560561 - Declare assumptions and invariants next to the associated code instead of
561562 gathering all properties in a single place.
562- - Declare a frequent code pattern which should be factorized in the
563- transition problem (see the section about Copilot limitations)
563+ - Declare a frequent code pattern that can be factorized in the transition
564+ problem (see the section about Copilot limitations)
564565
565566## FAQ
566567
567- ### Why does the code related to transition systems look so complex ?
568+ ### Why does the code related to transition systems look so complex?
568569
569- It is true the code of ` TransSys ` is quite complex. In fact, it would be really
570- straightforward to produce a flattened transition system and then a Kind2 file
571- with just a single * top* predicate. In fact, It would be as easy as producing
572- an * IL* specification.
570+ It is true that the code of ` TransSys ` is quite complex. In fact, it would be
571+ really straightforward to produce a flattened transition system and then a
572+ Kind2 file with just a single * top* predicate. In fact, It would be as easy as
573+ producing an * IL* specification.
573574
574575To be honest, I'm not sure producing a modular * Kind2* output is worth the
575576complexity added. It's especially true at the time I write this in the sense
576577that:
577578
578579* Each predicate introduced is used only one time (which is true because
579- copilot doesn't handle functions or parametrized streams like Lustre does and
580- everything is inlined during the reification process).
580+ Copilot doesn't handle functions or parameterized streams like Lustre does
581+ and everything is inlined during the reification process).
581582* A similar form of structure could be obtained from a flattened Kind2 native
582583 input file with some basic static analysis by producing a dependency graph
583584 between variables.
584- * For now, the * Kind2* model-checker ignores these structure informations .
585+ * For now, the * Kind2* model-checker ignores these structures .
585586
586587However, the current code offers some nice transformation tools (node merging,
587588` Renaming ` monad...) which could be useful if you intend to write a tool for
0 commit comments