Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Enumerator<T> trait #37

Draft
wants to merge 69 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
6a32b86
Partial work, almost verifying
robin-aws Feb 2, 2022
0be0251
Got an iterator -> enumerator adaptor to verify
robin-aws Feb 5, 2022
6c2088a
Add Enumerator.Done()
robin-aws Feb 8, 2022
2545eae
More partial work
robin-aws Feb 9, 2022
6baa11f
Stepper approach seems to work better!
robin-aws Feb 12, 2022
73cc542
More partially-implemented enumerators
robin-aws Feb 13, 2022
768df4e
Restricted Step post-condition and got Filter to verify!
robin-aws Feb 13, 2022
c7aaacc
Defined Rust-style Iterator
robin-aws Feb 14, 2022
dc59bea
Improve while loop idiom, start cleaning up
robin-aws Feb 15, 2022
4b8307e
CLEANUP
robin-aws Feb 15, 2022
eee7d1d
Add some todos
robin-aws Feb 15, 2022
37015dd
(Mostly) converted Step() to Next()
robin-aws Feb 16, 2022
924d685
Progress on fixing Filter
robin-aws Feb 16, 2022
323abfd
Replace Done() with HasNext()
robin-aws Feb 16, 2022
aae5518
Partial progress on proving enumerator semantics
robin-aws Feb 18, 2022
2ebc186
Progress on fixing FilterEnumerator
robin-aws Feb 19, 2022
5e63373
Proved semantics of FilteredEnumerator
robin-aws Feb 19, 2022
a0e370f
Fixed termination guarantee on FilteredEnumerator
robin-aws Feb 19, 2022
bcfbdb1
Progress on iterator example
robin-aws Feb 19, 2022
9d52831
Fixed iterator adaptor example
robin-aws Feb 21, 2022
5b3b315
Relocating example code
robin-aws Feb 22, 2022
23395ac
Add Fold
robin-aws Feb 23, 2022
16142fb
Tons of documentation
robin-aws Feb 23, 2022
2b4b2f6
Bit more cleanup
robin-aws Feb 23, 2022
a090841
Add lit commands
robin-aws Feb 23, 2022
07132ad
Remove execution-created files
robin-aws Feb 23, 2022
ee50ca5
Pr feedback, fix build
robin-aws Feb 24, 2022
ef77a1c
Fix another lit test failure
robin-aws Feb 24, 2022
f172ef5
Trim redundant post-conditions
robin-aws Feb 24, 2022
08876c0
ACTUALLY fix lit test failure
robin-aws Feb 24, 2022
89984b4
Add trait specializations
robin-aws Feb 24, 2022
64f257f
Sigh
robin-aws Feb 24, 2022
a5c7863
Switching to Option<T> flavour
robin-aws Feb 25, 2022
e449f61
More verification progress and useful examples
robin-aws Feb 26, 2022
6a0c220
Fixed iterator adaptor, progress on other parts
robin-aws Mar 1, 2022
a811a1f
Added WithEnumeratedEnumerator
robin-aws Mar 1, 2022
a208cd4
Extra comment
robin-aws Mar 1, 2022
e68755c
Quick note
robin-aws Mar 2, 2022
4aca2f3
Merge branch 'master' of github.com:dafny-lang/libraries into enumera…
robin-aws May 3, 2023
256aabe
Update for Dafny 4, couple of notes
robin-aws May 3, 2023
2f2aa05
Trying out a pure-functional version
robin-aws May 8, 2023
5986181
Promising success with constant Next invariant
robin-aws May 10, 2023
47dcc9d
Partial attempt to use function instead of constant function value
robin-aws May 11, 2023
49de185
Generalizing to Actions
robin-aws May 14, 2023
a32f73b
Temporary Repr restriction
robin-aws May 15, 2023
377de50
Sweet solution for enumerator termination measure
robin-aws May 16, 2023
e38755c
SeqEnumerator
robin-aws May 17, 2023
5f6e167
Use height instead of Repr for termination, split up more files
robin-aws May 22, 2023
006a553
Progress
robin-aws May 23, 2023
f339ada
Remove unnecessary nested exists
robin-aws May 23, 2023
8c88b21
More progress
robin-aws May 23, 2023
4cae439
EnumeratedDistributesOverConcat!
robin-aws May 27, 2023
f321710
Partial work to fix definition of IsEnumerator()
robin-aws May 29, 2023
bd81878
Enumerators.dfy verifying again
robin-aws May 29, 2023
e066a2d
NoOp verifies!
robin-aws May 29, 2023
cf6ac72
Mapping enumerator verifies (avoiding Enumerated lemmas for now)
robin-aws May 29, 2023
300c012
Progress on composed action
robin-aws May 30, 2023
bad3054
Experimenting with no CanConsume
robin-aws Jun 7, 2023
bcb450d
Revert "Experimenting with no CanConsume"
robin-aws Jan 12, 2024
8139631
Complete EnumeratedDistributesOverConcat2
robin-aws Jan 12, 2024
8607ee7
More progress
robin-aws Jan 15, 2024
2efc280
Got ComposedAction to verify!
robin-aws Jan 15, 2024
259f899
Lots more Actions, partial refactoring for “history”
robin-aws Feb 17, 2024
b036c66
Got GenericAction to work as a base type
robin-aws Feb 21, 2024
627ec2a
Updated most cases to deal with history change
robin-aws Feb 22, 2024
abe9233
Improving combinator specs
robin-aws Feb 22, 2024
18a2ef4
Replace IsSmallerThan with DecreasesTo, motivating example
robin-aws Feb 22, 2024
fd231a1
Tweaks
robin-aws Feb 22, 2024
be37f10
added new type characteristic to get things to check
Mar 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
80 changes: 80 additions & 0 deletions examples/Enumerators/Datatypes.dfy
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// RUN: %dafny /compile:3 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"

include "Enumerators.dfy"

// An example of an enumerator that traverses the sub-values in
// an algebraic datatype value.
module DatatypeEnumerator {

import opened Enumerators

datatype Tree<T> = Node(left: Tree<T>, value: T, right: Tree<T>) | Nil

datatype TreeTraversal<T> = InorderTraversal(Tree<T>) /* | PreorderTraversal(Tree<T>) | PostorderTraversal(Tree<T>) */ {
method Enumerator() returns (e: Enumerator<T>)
ensures e.Valid()
ensures fresh(e.Repr)
ensures e.enumerated == []
{
match this
case InorderTraversal(tree) => {
e := InorderEnumerator(tree);
}
}
}

method InorderEnumerator<T>(tree: Tree<T>) returns (e: Enumerator<T>)
ensures e.Valid()
ensures fresh(e.Repr)
ensures e.enumerated == []
{
match tree
case Nil => {
e := new SeqEnumerator([]);
}
case Node(left, value, right) => {
// TODO: How to make this lazy?
var thisValueEnum := new SeqEnumerator([value]);
var leftEnum := InorderEnumerator(left);
var rightEnum := InorderEnumerator(right);
var tmp := new ConcatEnumerator(leftEnum, thisValueEnum);
e := new ConcatEnumerator(tmp, rightEnum);
}
}

method Main() {
var tree := Node(
Node(
Nil,
1,
Node(Nil, 2, Nil)
),
3,
Node(
Node(Nil, 4, Nil),
5,
Nil
)
);

var traversal := InorderTraversal(tree);
var e := traversal.Enumerator();
while true
invariant e.Valid() && fresh(e.Repr)
decreases e.Decreases()
{
var x := e.Next();
if x.None? { break; }

print x.value;
}

// With foreach loop support, the above could just be:
//
// foreach x in InorderTraversal(tree) {
// print x;
// }

}
}
3 changes: 3 additions & 0 deletions examples/Enumerators/Datatypes.dfy.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

Dafny program verifier finished with 5 verified, 0 errors
12345
225 changes: 225 additions & 0 deletions examples/Enumerators/Enumerators.dfy
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
// RUN: %dafny /compile:3 "%s" > "%t"
// RUN: %diff "%s.expect" "%t"

include "../../src/Enumerators/Enumerators.dfy"

module Demo {

import opened Enumerators
import opened Wrappers

// Note the common template for using a while loop on
// an arbitrary Enumerator. This will likely be what we
// propose that a "foreach" loop in Dafny will desugar to.

method Example1() {
var numbers := [1, 2, 3, 4, 5];

var e: Enumerator<int> := new SeqEnumerator(numbers);
while true
invariant e.Valid() && fresh(e.Repr)
decreases e.Decreases()
{
var element := e.Next();
if element.None? { break; }

print element.value, "\n";
}
}

method Example2() {
var first := [1, 2, 3, 4, 5];
var second := [6, 7, 8];
var e1 := new SeqEnumerator(first);
var e2 := new SeqEnumerator(second);
var e: Enumerator := new ConcatEnumerator(e1, e2);

while true
invariant e.Valid() && fresh(e.Repr)
decreases e.Decreases()
{
var element := e.Next();
if element.None? { break; }

print element.value, "\n";
}
}

method PrintWithCommas() {
var first := [1, 2, 3, 4, 5];
var e: Enumerator := new SeqEnumerator(first);

for index := 0 to *
invariant e.Valid() && fresh(e.Repr)
decreases e.Decreases()
{
var element := e.Next();
if element.None? { break; }

if index > 0 {
print ", ";
}
print element;
}
print "\n";
}

method MappingExample() {
var first := [1, 2, 3, 4, 5];
var e1 := new SeqEnumerator(first);
var e: Enumerator := new MappingEnumerator(x => x + 2, e1);

var result: seq<int> := [];
while true
invariant e.Valid() && fresh(e.Repr)
decreases e.Decreases()
{
var element := e.Next();
if element.None? { break; }

result := result + [element.value];
}
assert e.enumerated == Seq.Map(x => x + 2, first);
}

// Some examples showing the equivalence between enumerator
// operations and sequence operations.

method RoundTrip(s: seq<nat>) returns (result: seq<nat>)
ensures result == s
{
var e: Enumerator := new SeqEnumerator(s);
result := CollectToSeq(e);
}

// method ToArray(s: seq<nat>) returns (result: array<nat>)
// ensures result[..] == s
// {
// var e: Enumerator := new SeqEnumerator(s);
// assert Sized(e, |s|);
// result := CollectToArray(e, |s|);
// }

method AddTwoToEach(s: seq<nat>) returns (result: seq<nat>)
ensures result == Seq.Map(x => x + 2, s)
{
var e := new SeqEnumerator(s);
var mapped := new MappingEnumerator(x => x + 2, e);
result := CollectToSeq(mapped);
}

method SetToSeq<T>(s: set<T>) returns (result: seq<T>)
ensures multiset(result) == multiset(s)
{
var e := new SetEnumerator(s);
result := CollectToSeq(e);
}

method SeqEnumerator<T>(s: seq<T>) returns (r: Enumerator<T>)
ensures r.Valid() && fresh(r.Repr)
// ensures Sized(r, |s|)
{
r := new SeqEnumerator(s);
}

// method SizedExample<T>()
// {
// var e: Enumerator := SeqEnumerator([1,2,3,4,5]);
// assert Sized(e, 5);
// var one := e.Next();
// assert Sized(e, 5);
// }


method Filter<T>(s: seq<T>, p: T -> bool) returns (result: seq<T>)
ensures result == Seq.Filter(p, s)
{
var e := new SeqEnumerator(s);
var filtered: FilteredEnumerator := new FilteredEnumerator(e, p);
result := CollectToSeq(filtered);
assert e.enumerated == s;
}

method Concatenate<T>(first: seq<T>, second: seq<T>) returns (result: seq<T>)
ensures result == first + second
{
var e1 := new SeqEnumerator(first);
var e2 := new SeqEnumerator(second);
var concatenated: ConcatEnumerator := new ConcatEnumerator(e1, e2);
result := CollectToSeq(concatenated);
assert concatenated.Valid();
}

// method ConcatenateToArray<T(0)>(first: seq<T>, second: seq<T>) returns (result: array<T>)
// ensures result[..] == first + second
// {
// var e1 := new SeqEnumerator(first);
// var e2 := new SeqEnumerator(second);
// var concatenated: ConcatEnumerator := new ConcatEnumerator(e1, e2);
// concatenated.CountOfResult(|first|, |second|);
// result := CollectToArray(concatenated, |first| + |second|);
// assert concatenated.Valid();
// }

class Cell {
constructor() {}
}

// An enumerator that produces newly allocated objects.
// Test case for framing.
class CellEnumerator extends Enumerator<Cell> {
var remaining: nat
predicate Valid()
reads this, Repr
ensures Valid() ==> this in Repr
decreases Repr, 0
{
&& this in Repr
}

function Decreases(): nat
reads this, Repr
requires Valid()
decreases Repr, 1
{
remaining
}

method Next() returns (element: Option<Cell>)
requires Valid()
modifies Repr
decreases Repr
ensures Valid()
ensures Repr <= old(Repr)
ensures ienumerated == old(ienumerated) + [element]
ensures element.Some? ==> (
&& Decreases() < old(Decreases())
&& enumerated == old(enumerated) + [element.value]
)
ensures element.None? ==> (
&& Decreases() == 0
&& enumerated == old(enumerated)
)
{
if remaining > 0 {
var cell := new Cell();
element := Some(cell);
remaining := remaining - 1;
} else {
element := None;
}
Enumerated(element);
}
}

method EnumerateForever() decreases * {
var allNats := new NatEnumerator();
while true
invariant allNats.Valid() && fresh(allNats.Repr)
decreases *
{
var next := allNats.Next();
print next;
}
}
}
2 changes: 2 additions & 0 deletions examples/Enumerators/Enumerators.dfy.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

Dafny program verifier finished with 11 verified, 0 errors