Skip to content

Commit 146749c

Browse files
author
Mark R. Tuttle
committed
Add a Random module to the dafny library.
The Dafny.Random module supports nextBool, nextInt, and nextReal returning arbitrary boolean, integer, and real values using the underlying random number generator of the target language (currently C#, Java, and JavaScript).
1 parent ae8708c commit 146749c

File tree

7 files changed

+218
-0
lines changed

7 files changed

+218
-0
lines changed

examples/Random/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
random.cs
2+
random.dll
3+
random.jar
4+
random.js
5+
random.runtimeconfig.json
6+
node_modules
7+
package-lock.json
8+
package.json

examples/Random/random.dfy

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*******************************************************************************
2+
* Copyright by the contributors to the Dafny Project
3+
* SPDX-License-Identifier: MIT
4+
*******************************************************************************/
5+
6+
// RUN: %verify "%s"
7+
8+
// #RUN: %run --no-verify --target:cs "%s" --input "%S/../../src/Random/Random.cs"
9+
// #RUN: %run --no-verify --target:java "%s" --input "%S/../../src/Random/Random.java"
10+
// #RUN: %run --no-verify --target:js "%s" --input "%S/../../src/Random/Random.js"
11+
12+
include "../../src/Random/Random.dfy"
13+
14+
module RandomExample {
15+
import Dafny.Random
16+
17+
method Main(args: seq<string>) {
18+
var verbose := false;
19+
if |args| > 1 { verbose := args[1] == "--verbose"; }
20+
21+
var bvalue := Random.nextBool();
22+
if verbose { print "Random boolean: ", bvalue, "\n"; }
23+
24+
var bound := 10;
25+
var ivalue := Random.nextInt(bound);
26+
if verbose { print "Random integer from [0,", bound, "): ", ivalue, "\n"; }
27+
expect 0 <= ivalue < bound, "Random integer not within bounds [0,bound)";
28+
29+
var lvalue := Random.nextInt();
30+
if verbose { print "Random integer from [0,MAXINT): ", lvalue, "\n"; }
31+
expect 0 <= ivalue, "Random unbounded integer not within bounds [0,MAXINT)";
32+
33+
var rvalue: real := Random.nextReal();
34+
if verbose { print "Random real from [0,1): ", rvalue, "\n"; }
35+
expect 0.0 <= rvalue < 1.0, "Random real not within [0,1)";
36+
}
37+
}

src/Random/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Dafny.Random
2+
3+
## Overview
4+
5+
The `Dafny.Random` module provides a uniform interface to random values across target languages.
6+
7+
```
8+
// Return an arbitrary boolean value
9+
predicate nextBool()
10+
11+
// Return an arbitrary integer value in the range [0, bound)
12+
function nextInt(bound: int := 0): (value: int)
13+
ensures 0 <= value
14+
ensures bound > 0 ==> value < bound
15+
16+
// Return an arbitrary real value in the range [0,1)
17+
function nextReal(): (value: real)
18+
ensures 0.0 <= value < 1.0
19+
```
20+
To see the need for a uniform interface to probability,
21+
C# provides random integer values and Java and JavaScript provide random real values,
22+
and Dafny actually models real numbers as rationals with integral numerators and denominators
23+
of arbitrary size.
24+
This module gives one interface to these various sources of randomness.
25+
This is a simple interface to probability.
26+
For a more sophisticated treatment of probability, see
27+
the [Verified Monte Carlo (VMC)](https://github.com/dafny-lang/Dafny-VMC) library.
28+
29+
The `Dafny.Random` module also provides a uniform interface to nondeterminism and probability.
30+
For example, `nextInt(10)` returns an arbitrary integer from [0,10), but
31+
32+
* in a proof context, the integer is chosen nondeterministcally, and
33+
* in a compiled code context, the integer is chose probabilistically according to the uniform probability distribution.
34+
35+
Compare this with the Dafny construct `var value: int := *;` where value is arbitrary in a proof context and
36+
constant (typically 0) in compiled code.
37+
38+
## Usage
39+
40+
The `Random` module, like `FileIO` will not compile or run correctly without a language-specific
41+
implementation file. Implementations are currently provided for C#, Java, and JavaScript.
42+
To use `Random` in your code, you must:
43+
* `include` and `import` the `Random` module as you would any other library module
44+
* incorporate the corresponding language-specific implementation file (e.g. `Random.cs`) when building or running your program
45+
46+
The example [random.dfy](../../examples/Random/random.dfy) in the `examples` directory
47+
shows how to use the module.
48+
From the [examples](../../examples/Random) directory, compile and run the file `random.dfy` with
49+
50+
```bash
51+
# C#
52+
$ dafny run random.dfy --target cs --input ../../src/Random/Random.cs -- --verbose
53+
54+
# Java
55+
$ dafny run random.dfy --target java --input ../../src/Random/Random.java -- --verbose
56+
57+
# JavaScript
58+
$ dafny run random.dfy --target js --input ../../src/Random/Random.js -- --verbose
59+
```
60+
61+
If you aren't using `dafny run` to run your program, you will have to integrate the
62+
appropriate language-specific implementation file into your build system.

src/Random/Random.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*******************************************************************************
2+
* Copyright by the contributors to the Dafny Project
3+
* SPDX-License-Identifier: MIT
4+
*******************************************************************************/
5+
6+
namespace DafnyLibraries
7+
{
8+
using Dafny;
9+
using System;
10+
using System.Numerics;
11+
public class Random
12+
{
13+
private static System.Random rnd = new System.Random();
14+
public static bool nextBool()
15+
{
16+
return rnd.Next(2) == 0;
17+
}
18+
public static BigInteger nextInt(BigInteger Bound)
19+
{
20+
Int64 bound = (Int64) Bound;
21+
Int64 bnd = bound > 0 ? bound : Int64.MaxValue;
22+
return new BigInteger(rnd.NextInt64(bound));
23+
}
24+
public static BigRational nextReal()
25+
{
26+
Int64 num = rnd.NextInt64();
27+
Int64 den = Int64.MaxValue;
28+
return new BigRational(new BigInteger(num), new BigInteger(den));
29+
}
30+
}
31+
}

src/Random/Random.dfy

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*******************************************************************************
2+
* Copyright by the contributors to the Dafny Project
3+
* SPDX-License-Identifier: MIT
4+
*******************************************************************************/
5+
6+
module Dafny.Random {
7+
8+
// Return an arbitrary boolean value.
9+
predicate {:extern "DafnyLibraries.Random", "nextBool"} nextBool()
10+
11+
// Return an arbitrary integer value in the range [0, bound). If the optional
12+
// bound is omitted, return an arbitrary nonnegative integer (generally limited
13+
// to a 64-bit integer value when compiled).
14+
function {:extern "DafnyLibraries.Random", "nextInt"} nextInt(bound: int := 0): (value: int)
15+
ensures 0 <= value
16+
ensures bound > 0 ==> value < bound
17+
18+
// Return an arbitrary real value in the range [0,1).
19+
function {:extern "DafnyLibraries.Random", "nextReal"} nextReal(): (value: real)
20+
ensures 0.0 <= value < 1.0
21+
22+
}

src/Random/Random.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*******************************************************************************
2+
* Copyright by the contributors to the Dafny Project
3+
* SPDX-License-Identifier: MIT
4+
*******************************************************************************/
5+
6+
package DafnyLibraries;
7+
8+
import dafny.*;
9+
import java.math.*;
10+
11+
public class Random {
12+
public static boolean nextBool()
13+
{
14+
return Math.random() < 0.5;
15+
}
16+
public static BigInteger nextInt(BigInteger Bound)
17+
{
18+
long bound = Bound.longValue();
19+
long bnd = bound > 0 ? bound : Long.MAX_VALUE;
20+
return BigInteger.valueOf((long) Math.floor(Math.random() * bnd));
21+
}
22+
public static BigRational nextReal()
23+
{
24+
long num = (long) Math.floor(Math.random() * Long.MAX_VALUE);
25+
long den = Long.MAX_VALUE;
26+
return new BigRational(BigInteger.valueOf(num), BigInteger.valueOf(den));
27+
}
28+
}

src/Random/Random.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*******************************************************************************
2+
* Copyright by the contributors to the Dafny Project
3+
* SPDX-License-Identifier: MIT
4+
*******************************************************************************/
5+
6+
var DafnyLibraries = DafnyLibraries || {};
7+
DafnyLibraries.Random = (function () {
8+
let $module = {};
9+
10+
$module.nextBool = function () {
11+
try {
12+
return Math.random() < 0.5;
13+
} catch (e) {
14+
throw "nextBool failed";
15+
}
16+
}
17+
18+
$module.nextInt = function (bound) {
19+
bound = bound > 0 ? bound : Number.MAX_SAFE_INTEGER;
20+
return new BigNumber(Math.floor(Math.random() * bound));
21+
}
22+
23+
$module.nextReal = function () {
24+
num = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
25+
den = Number.MAX_SAFE_INTEGER;
26+
return new _dafny.BigRational(new BigNumber(num), new BigNumber(den));
27+
}
28+
29+
return $module;
30+
})();

0 commit comments

Comments
 (0)