-
Notifications
You must be signed in to change notification settings - Fork 25
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
Add a Random module to the dafny library #151
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
random.cs | ||
random.dll | ||
random.jar | ||
random.js | ||
random.runtimeconfig.json | ||
node_modules | ||
package-lock.json | ||
package.json |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/******************************************************************************* | ||
* Copyright by the contributors to the Dafny Project | ||
* SPDX-License-Identifier: MIT | ||
*******************************************************************************/ | ||
|
||
// RUN: %verify "%s" | ||
|
||
// #RUN: %run --no-verify --target:cs "%s" --input "%S/../../src/Random/Random.cs" | ||
// #RUN: %run --no-verify --target:java "%s" --input "%S/../../src/Random/Random.java" | ||
// #RUN: %run --no-verify --target:js "%s" --input "%S/../../src/Random/Random.js" | ||
|
||
include "../../src/Random/Random.dfy" | ||
|
||
module RandomExample { | ||
import Dafny.Random | ||
|
||
method Main(args: seq<string>) { | ||
var verbose := false; | ||
if |args| > 1 { verbose := args[1] == "--verbose"; } | ||
|
||
var bvalue := Random.nextBool(); | ||
if verbose { print "Random boolean: ", bvalue, "\n"; } | ||
|
||
var bound := 10; | ||
var ivalue := Random.nextInt(bound); | ||
if verbose { print "Random integer from [0,", bound, "): ", ivalue, "\n"; } | ||
expect 0 <= ivalue < bound, "Random integer not within bounds [0,bound)"; | ||
|
||
var lvalue := Random.nextInt(); | ||
if verbose { print "Random integer from [0,MAXINT): ", lvalue, "\n"; } | ||
expect 0 <= ivalue, "Random unbounded integer not within bounds [0,MAXINT)"; | ||
|
||
var rvalue: real := Random.nextReal(); | ||
if verbose { print "Random real from [0,1): ", rvalue, "\n"; } | ||
expect 0.0 <= rvalue < 1.0, "Random real not within [0,1)"; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# Dafny.Random | ||
|
||
## Overview | ||
|
||
The `Dafny.Random` module provides a uniform interface to random values across target languages. | ||
|
||
``` | ||
// Return an arbitrary boolean value | ||
predicate nextBool() | ||
|
||
// Return an arbitrary integer value in the range [0, bound) | ||
function nextInt(bound: int := 0): (value: int) | ||
ensures 0 <= value | ||
ensures bound > 0 ==> value < bound | ||
|
||
// Return an arbitrary real value in the range [0,1) | ||
function nextReal(): (value: real) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does seem useful, but I'd call it something more specific, since the implicit bounds will be less obvious on the caller side. |
||
ensures 0.0 <= value < 1.0 | ||
``` | ||
To see the need for a uniform interface to probability, | ||
C# provides random integer values and Java and JavaScript provide random real values, | ||
and Dafny actually models real numbers as rationals with integral numerators and denominators | ||
of arbitrary size. | ||
This module gives one interface to these various sources of randomness. | ||
This is a simple interface to probability. | ||
For a more sophisticated treatment of probability, see | ||
the [Verified Monte Carlo (VMC)](https://github.com/dafny-lang/Dafny-VMC) library. | ||
|
||
The `Dafny.Random` module also provides a uniform interface to nondeterminism and probability. | ||
For example, `nextInt(10)` returns an arbitrary integer from [0,10), but | ||
|
||
* in a proof context, the integer is chosen nondeterministcally, and | ||
* in a compiled code context, the integer is chose probabilistically according to the uniform probability distribution. | ||
|
||
Compare this with the Dafny construct `var value: int := *;` where value is arbitrary in a proof context and | ||
constant (typically 0) in compiled code. | ||
|
||
## Usage | ||
|
||
The `Random` module, like `FileIO` will not compile or run correctly without a language-specific | ||
implementation file. Implementations are currently provided for C#, Java, and JavaScript. | ||
To use `Random` in your code, you must: | ||
* `include` and `import` the `Random` module as you would any other library module | ||
* incorporate the corresponding language-specific implementation file (e.g. `Random.cs`) when building or running your program | ||
|
||
The example [random.dfy](../../examples/Random/random.dfy) in the `examples` directory | ||
shows how to use the module. | ||
From the [examples](../../examples/Random) directory, compile and run the file `random.dfy` with | ||
|
||
```bash | ||
# C# | ||
$ dafny run random.dfy --target cs --input ../../src/Random/Random.cs -- --verbose | ||
|
||
# Java | ||
$ dafny run random.dfy --target java --input ../../src/Random/Random.java -- --verbose | ||
|
||
# JavaScript | ||
$ dafny run random.dfy --target js --input ../../src/Random/Random.js -- --verbose | ||
``` | ||
|
||
If you aren't using `dafny run` to run your program, you will have to integrate the | ||
appropriate language-specific implementation file into your build system. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/******************************************************************************* | ||
* Copyright by the contributors to the Dafny Project | ||
* SPDX-License-Identifier: MIT | ||
*******************************************************************************/ | ||
|
||
namespace DafnyLibraries | ||
{ | ||
using Dafny; | ||
using System; | ||
using System.Numerics; | ||
public class Random | ||
{ | ||
private static System.Random rnd = new System.Random(); | ||
public static bool nextBool() | ||
{ | ||
return rnd.Next(2) == 0; | ||
} | ||
public static BigInteger nextInt(BigInteger Bound) | ||
{ | ||
Int64 bound = (Int64) Bound; | ||
Int64 bnd = bound > 0 ? bound : Int64.MaxValue; | ||
return new BigInteger(rnd.NextInt64(bound)); | ||
} | ||
public static BigRational nextReal() | ||
{ | ||
Int64 num = rnd.NextInt64(); | ||
Int64 den = Int64.MaxValue; | ||
return new BigRational(new BigInteger(num), new BigInteger(den)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/******************************************************************************* | ||
* Copyright by the contributors to the Dafny Project | ||
* SPDX-License-Identifier: MIT | ||
*******************************************************************************/ | ||
|
||
module {:options "-functionSyntax:4"} Dafny.Random { | ||
|
||
// Return an arbitrary boolean value. | ||
predicate {:extern "DafnyLibraries.Random", "nextBool"} nextBool() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Soundness issue: none of these can be predicates/functions since they are nondeterminisitic. Otherwise Dafny can prove false: if nextReal() != nextReal() {
print 1/0; // Verifies but crashes at runtime almost 100% of the time
}
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Credit to @seebees for spotting this while I was distracted by other things. :P There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Team effort, came in this AM to make sure this was on the PR, thanks! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One can make them functions as long as they take a |
||
|
||
// Return an arbitrary integer value in the range [0, bound). If the optional | ||
// bound is omitted, return an arbitrary nonnegative integer (generally limited | ||
// to a 64-bit integer value when compiled). | ||
function {:extern "DafnyLibraries.Random", "nextInt"} nextInt(bound: int := 0): (value: int) | ||
ensures 0 <= value | ||
ensures bound > 0 ==> value < bound | ||
|
||
// Return an arbitrary real value in the range [0,1). | ||
function {:extern "DafnyLibraries.Random", "nextReal"} nextReal(): (value: real) | ||
ensures 0.0 <= value < 1.0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think such a function makes sense because a random real number is irrational with probability 1, but So my suggestion: What we could have instead is a uniform distribution on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's fine to have such a method, if a distribution is not specified. The spec is correct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right in that it satisfies the post-condition. But I would argue that the name |
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/******************************************************************************* | ||
* Copyright by the contributors to the Dafny Project | ||
* SPDX-License-Identifier: MIT | ||
*******************************************************************************/ | ||
|
||
package DafnyLibraries; | ||
|
||
import dafny.*; | ||
import java.math.*; | ||
|
||
public class Random { | ||
public static boolean nextBool() | ||
{ | ||
return Math.random() < 0.5; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not |
||
} | ||
public static BigInteger nextInt(BigInteger Bound) | ||
{ | ||
long bound = Bound.longValue(); | ||
long bnd = bound > 0 ? bound : Long.MAX_VALUE; | ||
return BigInteger.valueOf((long) Math.floor(Math.random() * bnd)); | ||
} | ||
public static BigRational nextReal() | ||
{ | ||
long num = (long) Math.floor(Math.random() * Long.MAX_VALUE); | ||
long den = Long.MAX_VALUE; | ||
return new BigRational(BigInteger.valueOf(num), BigInteger.valueOf(den)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/******************************************************************************* | ||
* Copyright by the contributors to the Dafny Project | ||
* SPDX-License-Identifier: MIT | ||
*******************************************************************************/ | ||
|
||
var DafnyLibraries = DafnyLibraries || {}; | ||
DafnyLibraries.Random = (function () { | ||
let $module = {}; | ||
|
||
$module.nextBool = function () { | ||
try { | ||
return Math.random() < 0.5; | ||
} catch (e) { | ||
throw "nextBool failed"; | ||
} | ||
Comment on lines
+13
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this more likely to fail than the others? |
||
} | ||
|
||
$module.nextInt = function (bound) { | ||
bound = bound > 0 ? bound : Number.MAX_SAFE_INTEGER; | ||
return new BigNumber(Math.floor(Math.random() * bound)); | ||
} | ||
|
||
$module.nextReal = function () { | ||
num = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); | ||
den = Number.MAX_SAFE_INTEGER; | ||
return new _dafny.BigRational(new BigNumber(num), new BigNumber(den)); | ||
} | ||
|
||
return $module; | ||
})(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not make
bound
a required argument? I can see only by looking at the implementation that the implicit default bound is target language dependent, and that doesn't seem terribly useful.