Defining lexicographic order in an inductive datatype - dafny

datatype T= C0
| C1(s:string)
| C2(d:T)
| C3(d:T)
| C4(d1:T,d2:T)
| C5(d1:T,d2:T)
predicate method leq_string(s1:string,s2:string)
{
s1 == "" || (s2 != "" && s1[0] <= s2[0] && ( s1[0] == s2[0] ==> leq_string(s1[1..],s2[1..])))
}
lemma antisym_leq_string_Lemma (s1:string, s2:string)
ensures (leq_string(s1,s2) && leq_string(s2,s1) ) ==> s1 == s2
{
if s1 != "" && s2 != "" { antisym_leq_string_Lemma(s1[1..],s2[1..]);}
}
predicate method leq_T (alpha:T, beta:T)
{
alpha == C0
||
(alpha.C1? && beta != C0 && (beta.C1? ==> leq_string(alpha.s,beta.s)))
||
(alpha.C2? && beta != C0 && !beta.C1? && (beta.C2? ==> leq_T(alpha.d,beta.d)))
||
(alpha.C3? && beta != C0 && !beta.C1? && !beta.C2? && (beta.C3? ==> leq_T(alpha.d,beta.d)))
||
(alpha.C4? && beta != C0 && !beta.C1? && !beta.C2? && !beta.C3?
&& (beta.C4? ==> (leq_T(alpha.d1,beta.d1)
&& (alpha.d1 == beta.d1 ==> leq_T(alpha.d2,beta.d2)))))
||
(alpha.C5? && beta != C0 && !beta.C1? && !beta.C2? && !beta.C3? && !beta.C4?
&& (beta.C5? ==> (leq_T(alpha.d1,beta.d1)
&& (alpha.d1 == beta.d1 ==> leq_T(alpha.d2,beta.d2)))))
}
lemma antisym_leq_T_Lemma (alpha:T,beta:T)
ensures (leq_T(alpha,beta) && leq_T(beta,alpha)) ==> alpha == beta
{
if leq_T(alpha,beta) && leq_T(beta,alpha)
{
if alpha.C1? { if beta.C1? {antisym_leq_string_Lemma(alpha.s,beta.s); }}
// Equivalent to:
/*match alpha
case C0 =>
case C1(s) => if beta.C1? { antisym_leq_string_Lemma(s,beta.s);}
case C2(phi) =>
case C3(phi) =>
case C4(phi,psi) =>
case C5(phi,psi) => */
//All inductive cases are automatically proved
}
}
Two questions:
1.- Is there some better way of defining a lexicographic order on datatype T in Dafny?
2.- I have defined a datatype like T but with many different unary constructors (like C2 and C3) that shares the name of the destructor, and also many binary ones (like C4 and C5) that also shares the names of the two destructors. I have also define the lexicographic order on this type, exactly as above but with all the neccesary cases. Then, in the corresponding proof of antisym_leq_T_Lemma, the match statement (as above, but with much more inductive cases) works perfectly, i.e. proves automatically all the inductive cases. However the equivalent "if" doesn't work. Moreover, adding "else {}", Dafny complains that this "else" doens'tprove the postcondition. Could you guess what could be the problem?
Thanks in advance.

I think your definitions look fine. Because leq_string is recursive, it's natural that proofs of its properties would need an inductive lemma like antisym_leq_string_Lemma. And since the proof of the C1/C1 case of antisym_leq_T_Lemma needs lemma antisym_leq_string_Lemma, your proof of antisym_leq_T_Lemma is more-or-less as short as you could hope for.
In the longer definition of T and the longer antisymmetry proof you're alluding to, Dafny will still try to call the induction hypothesis automatically. That may not always work, so it's possible you'll need to write it explicitly.
There are many ways to express the properties you have defined, and in the end it's probably mostly a matter of taste how you write them. Here is one alternative, where, for the sake of showing some variety, I've tried to include several variations on what you've done.
predicate method StringBelow(s: string, t: string) {
exists n :: 0 <= n <= |s| && n <= |t| && s[..n] == t[..n] && (n < |s| ==> n < |t| && s[n] < t[n])
}
lemma AntisymStringBelow(s: string, t: string)
requires StringBelow(s, t) && StringBelow(t, s)
ensures s == t
{
}
function method Family(alpha: T): nat {
match alpha
case C0 => 0
case C1(_) => 1
case C2(_) => 2
case C3(_) => 3
case C4(_, _) => 4
case C5(_, _) => 5
}
predicate method Below(alpha:T, beta:T)
{
match (alpha, beta)
case (C1(a), C1(b)) => StringBelow(a, b)
case (C2(a), C2(b)) => Below(a, b)
case (C3(a), C3(b)) => Below(a, b)
case (C4(a0, a1), C4(b0, b1)) => Below(a0, b0) && (a0 == b0 ==> Below(a1, b1))
case (C5(a0, a1), C5(b0, b1)) => Below(a0, b0) && (a0 == b0 ==> Below(a1, b1))
case (_, _) => Family(alpha) <= Family(beta)
}
lemma AntisymBelow(alpha: T, beta: T)
requires Below(alpha, beta) && Below(beta, alpha)
ensures alpha == beta
{
match (alpha, beta)
case (C1(a), C1(b)) => AntisymStringBelow(a, b);
case (C2(a), C2(b)) =>
case (C3(a), C3(b)) =>
case (C4(a0, a1), C4(b0, b1)) =>
case (C5(a0, a1), C5(b0, b1)) =>
case (_, _) =>
}
The proof of AntisymBelow mimics the cases in the definition of Below, which looks nice (I think).
Note that AntisymStringBelow is proved automatically and, since StringBelow is not recursive, does not need induction. Dafny can do the same proof automatically in the C1/C1 case, so you can delete the call to AntisymStringBelow there. In fact, that lets you remove the entire match as well:
lemma AntisymBelow(alpha: T, beta: T)
requires Below(alpha, beta) && Below(beta, alpha)
ensures alpha == beta
{
}
Again, if your real situation is more complicated, you may need to fill in some of the proof cases explicitly.

Related

Dafny cannot prove that 'a^2<=b^2*c^2' implies 'a<=b*c' for reals greater than 0

I want to test the following property in Dafny: a^2<=b^2*c^2 implies a<=b*c. It automatically checks this for (positive) integer numbers:
lemma powersVSsquares_integers(a:int, b:int, c:int)
requires a>=0 && b>=0 && c>= 0
ensures (a*a <= b*b*c*c) ==> (a<=b*c)
{}
Now, I want to test this for real numbers, so I try:
lemma powersVSsquares_reals(a:real, b:real, c:real)
requires a>=0.0 && b>=0.0 && c>=0.0
ensures (a*a <= b*b*c*c) ==> (a<=b*c)
{}
But Dafny cannot prove this. Thus, I start with the case where a>=1.0 & b>=1.0 & c>=1.0:
lemma powersVSsquares_reals1(a:real, b:real, c:real)
requires (a>=1.0 && b>=1.0 && c>=1.0)
ensures (a*a <= b*b*c*c) ==> (a<=b*c)
{
assert a <= (b*b*c*c)/a;
}
But not even assert a <= (b*b*c*c)/a; can be proven. So I have no idea on how to deal with this prove.
Can anyone provide lemma powersVSsquares_reals proven in Dafny? Can this be done without defining a symbolic square root function?
I think you will need some axioms of the reals that are not readily available in Dafny.
lemma increase(a: real, b: real, x: real)
requires x > 0.0
requires a > b
ensures a * x > b * x
{
}
lemma zero(a: real)
requires a*a == 0.0
ensures a == 0.0
{
if a != 0.0 {
if a > 0.0 {
increase(a, 0.0, a);
assert false;
} else if a < 0.0 {
increase(-a, 0.0, -a);
assert false;
}
}
}
Then you can use these axioms to do a regular proof. Here I choose the style of a proof by contradiction
lemma powersVSsquares_reals(a:real, b:real, c:real)
requires a>=0.0 && b>=0.0 && c>=0.0
ensures (a*a <= (b*b)*(c*c)) ==> (a<=b*c)
{
if a*a <= (b*b)*(c*c) {
if a == 0.0 {
return;
}
if b == 0.0 || c == 0.0 {
zero(a);
return;
}
assert a*a <= (b*c)*(b*c);
if a > (b*c) {
assert b * c > 0.0;
increase(a, b*c, b*c);
assert a * (b*c) > (b * c) * (b*c);
assert a > 0.0;
increase(a, b*c, a);
assert a * a > (b * c) * a;
assert a * a > (b * c) * (b*c);
assert false;
}
assert a<=b*c;
}
}

dafny matrix expressions and functions

I'm trying to define a matrix transpose method and functions in Dafny. I'm having difficulty defining the function version.
/** verifies **/
method transpose(matrix: array2<real>) returns (result: array2<real>)
ensures result.Length0 == matrix.Length1 && result.Length1 == matrix.Length0
ensures forall i, j :: 0 <= i < matrix.Length1 && 0 <= j < matrix.Length0 ==> result[i,j] == matrix[j,i]
{
result := new real[matrix.Length1, matrix.Length0]((i,j) reads matrix => if 0 <= i < matrix.Length1 && 0 <= j < matrix.Length0 then matrix[j,i] else 0.0);
assert result.Length0 == matrix.Length1;
assert result.Length1 == matrix.Length0;
}
/** says it is an invalid LogicalExpresion**/
function ftranspose(matrix: array2<real>): array2<real>
reads matrix
ensures ftranspose(matrix).Length0 == matrix.Length1 && ftranspose(matrix).Length1 == matrix.Length0
ensures forall i, j :: 0 <= i < matrix.Length1 && 0 <= j < matrix.Length0 ==> ftranspose(matrix)[i,j] == matrix[j,i]
{
new real[matrix.Length1, matrix.Length0]((i,j) reads matrix => if 0 <= i < matrix.Length1 && 0 <= j < matrix.Length0 then matrix[j,i] else 0.0)
}
I'm not quite sure why it says it is an invalid logical expression since in the method I am able to assign it to a variable, which makes me assume that it is an expression.
I can see here in the docs that
Array allocation is permitted in ghost contexts. If any expression used to specify a dimension or initialization value is ghost, then the new allocation can only be used in ghost contexts. Because the elements of an array are non-ghost, an array allocated in a ghost context in effect cannot be changed after initialization.
So it seems like I should be able to define a new array in a function. What is the correct syntax here?
Functions (even ghost functions) are not allowed to allocate memory or call methods, so calls to new cannot appear in function bodies.
This is because functions must be deterministic (return the same thing when called with the same arguments). As written, your function would return a different (fresh) object every time (reference types like arrays have reference equality, which means that they are the same if they live at the same address, not just if they have the same contents).
The passage you quoted is relevant for ghost methods, but does not apply to functions.
So the answer is 1. Don't use array which is heap based as Clément said. 2. Use datatypes. The following verifies...
datatype Matrix = Matrice(vals: seq<seq<real>>, rows: nat, columns: nat)
predicate isMatrix(mat: Matrix) {
mat.rows >= 1 && mat.columns >= 1 && |mat.vals| == mat.rows && forall i :: 0 <= i < mat.rows ==> |mat.vals[i]| == mat.columns
}
function method seqTranspose(mat: Matrix): Matrix
requires isMatrix(mat)
ensures isMatrix(seqTranspose(mat))
ensures seqTranspose(mat).columns == mat.rows
ensures seqTranspose(mat).rows == mat.columns
// ensures seqTranpose(matrix).Length0 == matrix.Length1 && ftranspose(matrix).Length1 == matrix.Length0
ensures forall i, j :: 0 <= i < mat.columns && 0 <= j < mat.rows ==> seqTranspose(mat).vals[i][j] == mat.vals[j][i]
{
Matrice(seq(mat.columns, i requires 0 <= i < mat.columns => seq(mat.rows, j requires 0 <= j < mat.rows => mat.vals[j][i])), mat.columns, mat.rows)
}
lemma matTranspose(mat: Matrix)
requires isMatrix(mat)
ensures seqTranspose(seqTranspose(mat)) == mat
{
assert forall i :: 0 <= i < |mat.vals| ==> mat.vals[i] == seqTranspose(seqTranspose(mat)).vals[i];
}

Is "or" defined differently for Mathematica and Mathics?

In Mathematica, "or" looks to be defined with a double vertical bar. See https://reference.wolfram.com/language/ref/Or.html or https://mathematica.stackexchange.com/a/119763.
In Mathics 2.1.0, that doesn't seem to work:
In[16]:= If[1<0 || 2<3 || 3<4, 0, 1]
Syntax::sntxf: "If[1<0 " cannot be followed by " 2<3  3<4, 0, 1]" (line 1 of "<stdin>").
whereas the word "or" seems to work:
In[16]:= If[1<0 or 2<3 or 3<4, 0, 1]
Out[16]= 1
So do I have to use || in Mathematica and or in Mathics, or am I mistaken?
Mathics has a documentation, though it's a pdf for some reason, https://mathics.org/docs/mathics-latest.pdf
Page 11 at the moment (part of 3. Language Tutorials):
Comparisons and Boolean Logic
[...]
Truth values can be negated using ! (logical not) and combined using && (logical and) and || (logical or):
>> !True
False
>> !False
True
>> 3 < 4 && 6 > 5
True
&& has higher precedence than ||, i.e. it binds stronger:
>> True && True || False && False
True
>> True && (True || False) && False
False
based on that I would indeed expect || and && to work.
Also, there is an example on page 44 with If[x == 0.0 && y <= 0, 0.0, Sin[x ^ y] + 1 / Min[x, 0.5]] + 0.5].
Your error message may be the key here as the || became those boxed question marks, which may be something about character encoding. If it is a file you are running, it may be worth checking if it's ASCII.

Simulate the semantics of x86 opcode 'bsf' in Z3

I am working on some assembly program analysis task using Z3. And I am trapped in simulating the semantics of x86 opcode bsf.
The semantics of bsf operand1 operand2 is defined as searches the source operand (operand1) for the least significant set bit (1 bit).
Its semantics can be simulated in C as:
if(operand1 == 0) {
ZF = 0;
operand2 = Undefined;
}
else {
ZF = 0;
Temporary = 0;
while(Bit(operand1, Temporary) == 0) {
Temporary = Temporary + 1;
operand2 = Temporary;
}
}
Right now, suppose each operand (e.g., register) maintains a symbolic expression, I am trying to simulate the above semantics in Z3Py. The code I wrote is something like this (simplified):
def aux_bsf(x): # x is operand1
if simplify(x == 0):
raise Exception("undefined in aux_bsf")
else:
n = x.size()
for i in range(n):
b = Extract(i, i, x)
if simplify(b == 1):
return BitVecVal(i, 32)
raise Exception("undefined in bsf")
However, I find that the evaluation of simplify(x==0) (e.g., x equals BitVecVal(13, 32) + BitVec("symbol1", 32),) is always equal to True. In other words, I am always trapped in the first exception!
Am I doing anything wrong here..?
====================================================
OK, so I think what I need is something like:
def aux_bsf(x):
def aux(x, i):
if i == 31:
return 31
else:
return If(Extract(i, i, x) == 1, i, aux(x, i+1))
return aux(x, 0)
simplify(x == 0) returns an expression, it does not return True/False, where False = 0. Python would treat an expression reference as a non-zero value and therefore take the first branch. Unless 'x' is a bit-vector constant, simplification would not return a definite value. The same issue is with simplify(b == 1).
You could encode such functions as a relation between operand1 and operand2, e.g., something along the lines of:
def aux_bsf(s, x, y):
for k in range(x.size()):
s.Add(Implies(lsb(k, x), y == k)
def lsb(k, x):
first0 = True
if k > 0:
first0 = Extract(x, k-1,0) == 0
return And(Extract(x,k,k) == 1, first0)
You can also use uninterpreted functions for the cases where aux_bsf is under-specified.
For example:
def aux_bsf(x):
bv = x.sort()
bsf_undef = Function('bsf-undef', bv, bv)
result = bsf_undef(x)
for k in reverse(range(bv.size()))
result = If(Extract(x, k, k) == 1), BitVecVal(k, bv), result)
return result
def reverse(xs):
....

Bound checks for array and string slices

I can't seem to get my head around the rules that govern these two cases:
1. The end index may be one less than the start index, producing an empty array/string.
2. It's apparently legal to position the start index just behind the last element, if the end index is one less, as before.
[|0..2|].[3..2];; // [||]
"bar".[3..2];; // ""
A naive implementation of bound checks with consideration of case 1 wouldn't allow case 2:
let trySlice i j (a : string) =
let lastIdx = a.Length - 1
if i < 0 || i > lastIdx || j < i - 1 || j > lastIdx then None
else Some a.[i..j]
trySlice 3 2 "bar" // None
What's the rationale behind this behavior? How to proceed?
Edit
This is what I have now thanks to Tarmil's input
let trySlice i j (a : string) =
if i >= 0 && j >= i - 1 && j < a.Length then Some a.[i..j]
else None
which should be equivalent to
let trySlice' i j (s : string) =
try s.Substring(i, j - i + 1) |> Some
with _ -> None
I suppose the rationale is that a.[i..j] is a sequence of length (j - i + 1), so in a way it makes sense to allow i = j + 1 as a way to extract an empty sequence.
As for "how to proceed", if you want your trySlice to accept all cases that the built-in slicing accepts, then just remove the i > lastIdx clause. When i = lastIdx + 1, the only way for the other conditions to pass is if j = lastIdx, and when i > lastIdx + 1, there is no way for j to pass both its constraints.
As a side-note, the way you write:
if (failCondition || failCondition || ...) then None else Some x
feels counter-intuitive to me for some reason, I would have written it as:
if (successCondition && successCondition && ...) then Some x else None

Resources