Skip to content

Commit 6ffd98a

Browse files
authored
Remove quantifier in postconditions (#144)
By submitting this pull request, I confirm that my contribution is made under the terms of the [MIT license](https://github.com/dafny-lang/dafny/blob/master/LICENSE.txt).
1 parent 9b15eba commit 6ffd98a

File tree

9 files changed

+122
-52
lines changed

9 files changed

+122
-52
lines changed

audit.log

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
src/DafnyVMC.dfy(23,6): UniformPowerOfTwoSample: Definition has `assume {:axiom}` statement in body.
22
src/Distributions/Bernoulli/Correctness.dfy(59,17): BernoulliCorrectnessCaseFalse: Declaration has explicit `{:axiom}` attribute.
3-
src/Distributions/BernoulliExpNeg/Correctness.dfy(228,6): CorrectnessLe1: Definition has `assume {:axiom}` statement in body.
4-
src/Distributions/BernoulliExpNeg/Correctness.dfy(336,17): Le1SeriesConvergesToExpNeg: Declaration has explicit `{:axiom}` attribute.
5-
src/Distributions/BernoulliExpNeg/Correctness.dfy(401,6): Le1LoopCutDecomposeProb: Definition has `assume {:axiom}` statement in body.
6-
src/Distributions/BernoulliExpNeg/Correctness.dfy(402,6): Le1LoopCutDecomposeProb: Definition has `assume {:axiom}` statement in body.
7-
src/Distributions/BernoulliExpNeg/Correctness.dfy(409,6): Le1LoopCutDecomposeProb: Definition has `assume {:axiom}` statement in body.
8-
src/Distributions/BernoulliExpNeg/Correctness.dfy(410,6): Le1LoopCutDecomposeProb: Definition has `assume {:axiom}` statement in body.
9-
src/Distributions/BernoulliExpNeg/Correctness.dfy(414,6): Le1LoopCutDecomposeProb: Definition has `assume {:axiom}` statement in body.
10-
src/Distributions/BernoulliExpNeg/Correctness.dfy(415,6): Le1LoopCutDecomposeProb: Definition has `assume {:axiom}` statement in body.
11-
src/Distributions/BernoulliExpNeg/Correctness.dfy(605,8): Le1LoopCorrectnessEq: Definition has `assume {:axiom}` statement in body.
12-
src/Distributions/BernoulliExpNeg/Correctness.dfy(606,8): Le1LoopCorrectnessEq: Definition has `assume {:axiom}` statement in body.
3+
src/Distributions/BernoulliExpNeg/Correctness.dfy(229,6): CorrectnessLe1: Definition has `assume {:axiom}` statement in body.
4+
src/Distributions/BernoulliExpNeg/Correctness.dfy(346,17): Le1SeriesConvergesToExpNeg: Declaration has explicit `{:axiom}` attribute.
5+
src/Distributions/BernoulliExpNeg/Correctness.dfy(411,6): Le1LoopCutDecomposeProb: Definition has `assume {:axiom}` statement in body.
6+
src/Distributions/BernoulliExpNeg/Correctness.dfy(412,6): Le1LoopCutDecomposeProb: Definition has `assume {:axiom}` statement in body.
7+
src/Distributions/BernoulliExpNeg/Correctness.dfy(419,6): Le1LoopCutDecomposeProb: Definition has `assume {:axiom}` statement in body.
8+
src/Distributions/BernoulliExpNeg/Correctness.dfy(420,6): Le1LoopCutDecomposeProb: Definition has `assume {:axiom}` statement in body.
9+
src/Distributions/BernoulliExpNeg/Correctness.dfy(424,6): Le1LoopCutDecomposeProb: Definition has `assume {:axiom}` statement in body.
10+
src/Distributions/BernoulliExpNeg/Correctness.dfy(425,6): Le1LoopCutDecomposeProb: Definition has `assume {:axiom}` statement in body.
11+
src/Distributions/BernoulliExpNeg/Correctness.dfy(615,8): Le1LoopCorrectnessEq: Definition has `assume {:axiom}` statement in body.
12+
src/Distributions/BernoulliExpNeg/Correctness.dfy(616,8): Le1LoopCorrectnessEq: Definition has `assume {:axiom}` statement in body.
1313
src/Distributions/BernoulliExpNeg/Model.dfy(112,10): Le1LoopTerminatesAlmostSurely: Definition has `assume {:axiom}` statement in body.
1414
src/Distributions/BernoulliExpNeg/Model.dfy(148,4): Le1LoopDivergenceProbabilityBound: Definition has `assume {:axiom}` statement in body.
1515
src/Distributions/BernoulliExpNeg/Model.dfy(161,8): Le1LoopDivergenceProbabilityBound: Definition has `assume {:axiom}` statement in body.
@@ -35,12 +35,12 @@ src/ProbabilisticProgramming/Independence.dfy(77,17): IsIndepImpliesMeasurableNa
3535
src/ProbabilisticProgramming/Independence.dfy(82,17): IsIndepImpliesIsIndepFunction: Declaration has explicit `{:axiom}` attribute.
3636
src/ProbabilisticProgramming/Independence.dfy(87,17): CoinIsIndep: Declaration has explicit `{:axiom}` attribute.
3737
src/ProbabilisticProgramming/Independence.dfy(91,17): ReturnIsIndep: Declaration has explicit `{:axiom}` attribute.
38-
src/ProbabilisticProgramming/Loops.dfy(376,17): EnsureWhileTerminates: Declaration has explicit `{:axiom}` attribute.
39-
src/ProbabilisticProgramming/Loops.dfy(381,17): EnsureWhileTerminatesAlmostSurelyViaLimit: Declaration has explicit `{:axiom}` attribute.
40-
src/ProbabilisticProgramming/Loops.dfy(399,17): WhileProbabilityViaLimit: Declaration has explicit `{:axiom}` attribute.
41-
src/ProbabilisticProgramming/Loops.dfy(416,17): UntilProbabilityFraction: Declaration has explicit `{:axiom}` attribute.
42-
src/ProbabilisticProgramming/Loops.dfy(448,4): EnsureUntilTerminatesAndForAll: Definition has `assume {:axiom}` statement in body.
43-
src/ProbabilisticProgramming/Loops.dfy(471,4): WhileIsIndep: Definition has `assume {:axiom}` statement in body.
38+
src/ProbabilisticProgramming/Loops.dfy(362,17): EnsureWhileTerminates: Declaration has explicit `{:axiom}` attribute.
39+
src/ProbabilisticProgramming/Loops.dfy(367,17): EnsureWhileTerminatesAlmostSurelyViaLimit: Declaration has explicit `{:axiom}` attribute.
40+
src/ProbabilisticProgramming/Loops.dfy(385,17): WhileProbabilityViaLimit: Declaration has explicit `{:axiom}` attribute.
41+
src/ProbabilisticProgramming/Loops.dfy(402,17): UntilProbabilityFraction: Declaration has explicit `{:axiom}` attribute.
42+
src/ProbabilisticProgramming/Loops.dfy(435,4): EnsureUntilTerminatesAndForAll: Definition has `assume {:axiom}` statement in body.
43+
src/ProbabilisticProgramming/Loops.dfy(458,4): WhileIsIndep: Definition has `assume {:axiom}` statement in body.
4444
src/ProbabilisticProgramming/RandomSource.dfy(50,17): ProbIsProbabilityMeasure: Declaration has explicit `{:axiom}` attribute.
4545
src/ProbabilisticProgramming/RandomSource.dfy(54,17): CoinHasProbOneHalf: Declaration has explicit `{:axiom}` attribute.
4646
src/ProbabilisticProgramming/RandomSource.dfy(61,17): MeasureHeadDrop: Declaration has explicit `{:axiom}` attribute.

src/Distributions/BernoulliExpNeg/Correctness.dfy

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module BernoulliExpNeg.Correctness {
1919
import Loops
2020
import Bernoulli
2121
import Model
22+
import Uniform
2223

2324
lemma Correctness(gamma: Rationals.Rational)
2425
requires 0 <= gamma.numer
@@ -301,7 +302,16 @@ module BernoulliExpNeg.Correctness {
301302
assert denom >= 1;
302303
var eventTrue := Monad.BitstreamsWithValueIn(Model.Le1LoopIter(gamma)((true, k)), iset{(true, k')});
303304
var eventTrue2 := iset s | Bernoulli.Model.Sample(gamma.numer, denom)(s).Equals(true);
304-
assert eventTrue == eventTrue2;
305+
assert eventTrue == eventTrue2 by {
306+
forall s
307+
ensures s in eventTrue <==> s in eventTrue2
308+
{
309+
reveal Bernoulli.Model.Sample();
310+
if Uniform.Model.Sample(denom)(s).Result? {
311+
Uniform.Model.SampleBound(denom, s);
312+
}
313+
}
314+
}
305315
assert Rand.prob(eventTrue2) == gamma.numer as real / denom as real by {
306316
Bernoulli.Correctness.BernoulliCorrectness(gamma.numer, denom, true);
307317
reveal Bernoulli.Correctness.BernoulliMass();

src/Distributions/BernoulliExpNeg/Equivalence.dfy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ module BernoulliExpNeg.Equivalence {
8888
Model.Le1Loop(gamma)((true, 0))(oldS);
8989
{ reveal CaseLe1LoopInvariant(); }
9090
Model.Le1Loop(gamma)((false, k))(s);
91-
{ reveal Model.Le1Loop(); }
91+
{ reveal Model.Le1Loop(); Loops.WhileInitialConditionNegated(Model.Le1LoopCondition, Model.Le1LoopIter(gamma), (false, k), s); }
9292
Monad.Result((false, k), s);
9393
}
9494
}

src/Distributions/Uniform/Equivalence.dfy

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,16 @@ module Uniform.Equivalence {
1717
reveal Model.Sample();
1818
Loops.UntilUnroll(Model.Proposal(n), Model.Accept(n), s);
1919
}
20+
21+
lemma SampleLifts(n: nat, u: nat, oldS: Rand.Bitstream, prevS: Rand.Bitstream, s: Rand.Bitstream)
22+
requires n > 0
23+
requires Model.Sample(n)(oldS) == Model.Sample(n)(prevS)
24+
requires Monad.Result(u, s) == Model.Proposal(n)(prevS)
25+
requires Model.Accept(n)(u)
26+
ensures Model.Sample(n)(oldS) == Monad.Result(u, s)
27+
{
28+
reveal Model.Sample();
29+
Model.SampleTerminates(n);
30+
Loops.UntilUnroll(Model.Proposal(n), Model.Accept(n), prevS);
31+
}
2032
}

src/Distributions/Uniform/Implementation.dfy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module Uniform.Implementation {
99
import Model
1010
import Interface
1111
import Equivalence
12+
import Loops
1213

1314
trait {:termination false} Trait extends Interface.Trait {
1415
method UniformSample(n: nat) returns (u: nat)
@@ -29,7 +30,7 @@ module Uniform.Implementation {
2930
prevS := s;
3031
u := UniformPowerOfTwoSample(2 * n);
3132
}
32-
reveal Model.Sample();
33+
Equivalence.SampleLifts(n, u, old(s), prevS, s);
3334
}
3435
}
3536
}

src/Distributions/Uniform/Model.dfy

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ module Uniform.Model {
1313
import Loops
1414
import UniformPowerOfTwo
1515

16+
/************
17+
Definitions
18+
************/
19+
1620
// Definition 49
17-
opaque ghost function Sample(n: nat): (f: Monad.Hurd<nat>)
21+
opaque ghost function Sample(n: nat): Monad.Hurd<nat>
1822
requires n > 0
19-
ensures forall s | f(s).Result? :: 0 <= f(s).value < n
2023
{
2124
SampleTerminates(n);
2225
Loops.Until(Proposal(n), Accept(n))
@@ -36,11 +39,14 @@ module Uniform.Model {
3639

3740
ghost function IntervalSample(a: int, b: int): (f: Monad.Hurd<int>)
3841
requires a < b
39-
ensures forall s | f(s).Result? :: a <= f(s).value <= b
4042
{
4143
Monad.Map(Sample(b - a), x => a + x)
4244
}
4345

46+
/*******
47+
Lemmas
48+
*******/
49+
4450
lemma SampleTerminates(n: nat)
4551
requires n > 0
4652
ensures
@@ -87,4 +93,23 @@ module Uniform.Model {
8793
Loops.EnsureUntilTerminates(Proposal(n), Accept(n));
8894
}
8995
}
96+
97+
lemma SampleBound(n: nat, s: Rand.Bitstream)
98+
requires n > 0
99+
requires Sample(n)(s).Result?
100+
ensures 0 <= Sample(n)(s).value < n
101+
{
102+
reveal Sample();
103+
SampleTerminates(n);
104+
Loops.UntilResultIsAccepted(Proposal(n), Accept(n), s);
105+
}
106+
107+
lemma IntervalSampleBound(a: int, b: int, s: Rand.Bitstream)
108+
requires a < b
109+
requires IntervalSample(a, b)(s).Result?
110+
ensures a <= IntervalSample(a, b)(s).value < b
111+
{
112+
SampleBound(b-a, s);
113+
}
114+
90115
}

src/ProbabilisticProgramming/Loops.dfy

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -49,27 +49,15 @@ module Loops {
4949
// This definition is opaque because the details are not very useful.
5050
// For proofs, use the lemma `WhileUnroll`.
5151
// Equation (3.25), but modified to use `Monad.Diverging` instead of HOL's `arb` in case of nontermination
52-
opaque ghost function While<A(!new)>(condition: A -> bool, body: A -> Monad.Hurd<A>): (loop: A -> Monad.Hurd<A>)
53-
ensures forall s: Rand.Bitstream, init: A :: !condition(init) ==> loop(init)(s) == Monad.Return(init)(s)
54-
ensures forall s: Rand.Bitstream, init: A | loop(init)(s).Result? :: !condition(loop(init)(s).value)
55-
{
56-
var f :=
57-
(init: A) =>
58-
(s: Rand.Bitstream) =>
59-
if WhileTerminatesOn(condition, body, init, s)
60-
then
61-
var fuel := LeastFuel(condition, body, init, s);
62-
WhileCut(condition, body, init, fuel)(s)
63-
else
64-
Monad.Diverging;
65-
assert forall s: Rand.Bitstream, init: A :: !condition(init) ==> f(init)(s) == Monad.Return(init)(s) by {
66-
forall s: Rand.Bitstream, init: A ensures !condition(init) ==> f(init)(s) == Monad.Return(init)(s) {
67-
if !condition(init) {
68-
assert WhileCutTerminatesWithFuel(condition, body, init, s)(0);
69-
}
70-
}
71-
}
72-
f
52+
opaque ghost function While<A(!new)>(condition: A -> bool, body: A -> Monad.Hurd<A>): A -> Monad.Hurd<A> {
53+
(init: A) =>
54+
(s: Rand.Bitstream) =>
55+
if WhileTerminatesOn(condition, body, init, s)
56+
then
57+
var fuel := LeastFuel(condition, body, init, s);
58+
WhileCut(condition, body, init, fuel)(s)
59+
else
60+
Monad.Diverging
7361
}
7462

7563
ghost function LeastFuel<A>(condition: A -> bool, body: A -> Monad.Hurd<A>, init: A, s: Rand.Bitstream): (fuel: nat)
@@ -103,14 +91,8 @@ module Loops {
10391
// Definition of until loops (rejection sampling).
10492
// For proofs, use the lemma `UntilUnroll`.
10593
// Definition 44
106-
ghost function Until<A(!new)>(proposal: Monad.Hurd<A>, accept: A -> bool): (f: Monad.Hurd<A>)
94+
ghost function Until<A(!new)>(proposal: Monad.Hurd<A>, accept: A -> bool): Monad.Hurd<A>
10795
requires UntilTerminatesAlmostSurely(proposal, accept)
108-
ensures
109-
var reject := (a: A) => !accept(a);
110-
var body := (a: A) => proposal;
111-
forall s :: f(s) == proposal(s).Bind(While(reject, body))
112-
ensures
113-
forall s | f(s).Result? :: accept(f(s).value)
11496
{
11597
reveal While();
11698
var reject := (a: A) => !accept(a);
@@ -261,6 +243,7 @@ module Loops {
261243
unrolled;
262244
}
263245
} else {
246+
WhileInitialConditionNegated(condition, body, init, s);
264247
calc {
265248
loop;
266249
Monad.Result(init, s);
@@ -276,6 +259,9 @@ module Loops {
276259
ensures loop == unrolled == Monad.Diverging
277260
{
278261
reveal While();
262+
if !condition(init) {
263+
WhileInitialConditionNegated(condition, body, init, s);
264+
}
279265
match body(init)(s)
280266
case Diverging =>
281267
assert unrolled == Monad.Diverging;
@@ -432,6 +418,7 @@ module Loops {
432418
{
433419
var reject := (a: A) => !accept(a);
434420
var body := (a: A) => proposal;
421+
UntilAsWhile(proposal, accept, s);
435422
match proposal(s)
436423
case Diverging =>
437424
case Result(init, s') => WhileUnroll(reject, body, init, s');
@@ -483,4 +470,37 @@ module Loops {
483470
}
484471
Independence.BindIsIndep(proposal, While(reject, body));
485472
}
473+
474+
lemma WhileNegatesCondition<A>(condition: A -> bool, body: A -> Monad.Hurd<A>, init: A, s: Rand.Bitstream)
475+
requires While(condition, body)(init)(s).Result?
476+
ensures !condition(While(condition, body)(init)(s).value)
477+
{
478+
reveal While();
479+
}
480+
481+
lemma WhileInitialConditionNegated<A>(condition: A -> bool, body: A -> Monad.Hurd<A>, init: A, s: Rand.Bitstream)
482+
requires !condition(init)
483+
ensures While(condition, body)(init)(s) == Monad.Return(init)(s)
484+
{
485+
reveal While();
486+
assert WhileCutTerminatesWithFuel(condition, body, init, s)(0);
487+
}
488+
489+
lemma UntilResultIsAccepted<A>(proposal: Monad.Hurd<A>, accept: A -> bool, s: Rand.Bitstream)
490+
requires UntilTerminatesAlmostSurely(proposal, accept)
491+
requires Until(proposal, accept)(s).Result?
492+
ensures accept(Until(proposal, accept)(s).value)
493+
{
494+
reveal While();
495+
}
496+
497+
lemma UntilAsWhile<A>(proposal: Monad.Hurd<A>, accept: A -> bool, s: Rand.Bitstream)
498+
requires UntilTerminatesAlmostSurely(proposal, accept)
499+
ensures
500+
var reject := (a: A) => !accept(a);
501+
var body := (a: A) => proposal;
502+
Until(proposal, accept)(s) == proposal(s).Bind(While(reject, body))
503+
{
504+
reveal While();
505+
}
486506
}

src/Util/FisherYates/Correctness.dfy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ module FisherYates.Correctness {
269269
var zs := Model.Shuffle(xs, i)(s).value;
270270
assert zs[i..] == p[i..];
271271
var k := Uniform.Model.IntervalSample(i, |xs|)(s).value;
272+
Uniform.Model.IntervalSampleBound(i, |xs|, s);
272273
var s' := Uniform.Model.IntervalSample(i, |xs|)(s).rest;
273274
assert s in Monad.BitstreamsWithValueIn(h, A) by {
274275
var ys' := Model.Swap(xs, i, k);

src/Util/FisherYates/Model.dfy

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ module FisherYates.Model {
2020
{
2121
(s: Rand.Bitstream) =>
2222
if |xs[i..]| > 1 then
23-
var (j, s) :- Uniform.Model.IntervalSample(i, |xs|)(s);
23+
var (j, s') :- Uniform.Model.IntervalSample(i, |xs|)(s);
24+
assert i <= j < |xs| by { Uniform.Model.IntervalSampleBound(i, |xs|, s); }
2425
var ys := Swap(xs, i, j);
25-
Shuffle(ys, i + 1)(s)
26+
Shuffle(ys, i + 1)(s')
2627
else
2728
Monad.Return(xs)(s)
2829
}

0 commit comments

Comments
 (0)