with the Rust programming language
CHOOSE
Formally verify vector 2-norm
Verus Installation was relatively straight forward. The Rust compiler could not find the Verus package. This did not affect Verus verifiying code, but rendered issues within the Rust toolchain. Ultimately, Verus proved to lack support of floats (decimals)
Aeneas Simple to install (like verus), but with an extra dependency on a related project. Lacks support of floats (decimals)
coq-of-rust Easy installation using Rust cargo tool. Same issue as Aeneas (and Verus)
Property-based Testing / Model-checking Not as rigorous as a formal proof, but far more robust than nothing. Kani is a viable library.
(in progress)
Creusot Straight-forward installation. Provides annotations that appear to support floats and translates Rust to WhyML.
(in progress)
Environment Develop container-based environment work writing Rust code that has Why3 with provers, Coq, and Lean installed
...the use of tools that mathematically analyze the space of possible behaviors of a design, rather than computing results for particular values.
...a powerful technique used to mathematically prove that an appropriately scaled model of a system does or does not exhibit desirable properties. [1]
Testing is a poor substitute for proof.
Category Theory for Programmers
[A] proof is rigorous when it is (or could be) written out in the first-order predicate language… as a sequence of inferences from the axioms ZFC, each inference made according to one of the stated rules…
Formally Verified Mathematics [2]
I verified some code
I don't need to understand linear algebra, OCaml, Coq, Why3, Rust, and the de Bruijn Criterion to understand your project, right?
I don't have to, right?
"formality gap"
Model
Implementation
"Math" world
"Real" world
< />
Removes formality gap
Single code base
Well known language
Better than nothing
Removes formality gap
Single code base
Well known language
Better than nothing
Removes formality gap
Single code base
Well known language
Better than nothing
code translation
"Math" world
"Real" world
"Math" world
"Real" world
Intermediate
Representation
Why3
"Real" world
"Math" world
& coq-of-rust
Formally verify Rust
Supports float type
Popular backends
Rigorous verification
Formally verify Rust
Supports float type
Popular backends
Rigorous verification
Formally verify Rust
Supports float type
Popular backends
Rigorous verification
Summary: Verification toolchain for Rust programs. Built with OCaml and has F*, Lean, Coq, and HOL backends...relies on a translation from Rust's mid-level intermediate representation (MIR) internal language to a pure lambda calculus
Issues: Only supports integers and natural numbers
Summary: Tool for deductive verification of Rust code. It allows you to annotate your code with specifications, invariants and assertions and then verify them formally and automatically, returning a proof that your code satisfies the specs.
Issues: NSTR (in progress)
Summary: Tool for verifying the correctness of code written in Rust. Developers write specifications of what their code should do, and Verus statically checks that the executable Rust code will always satisfy the specifications for all possible executions of the code
Issues: Only supports integers and natural numbers
docker run -it \
--privileged \
--name notebook \
--hostname $(hostname) \
-p 1337:1337 \
ghcr.io/jhwohlgemuth/lambda
# Verify Coq opam packages are installed
opam list | grep coq
# Verify Coq version (installed with opam)
coqc --version
# Read Lean help
lean --help
# View installed Why3 provers
why3 config list-provers
# https://github.com/AeneasVerif/aeneas
install_aeneas
aeneas --help
# https://github.com/xldenis/creusot/tree/master
install_creusot
cargo --list | grep creusot
# https://github.com/verus-lang/verus
install_verus
verus --help
https://localhost:1337
GitHub repository (jhwohlgemuth/verified-linear-algebra)
Summary: Bit-precise model checker for Rust. Verifies memory safety, the absence of some types of unexpected behavior (e.g., arithmetic overflows), user inserted assert!, and more. Somewhere in between property-based testing and formal verification.
Issues: Not rigorous verification
# Install
cargo install --locked kani-verifier
# Configure
cargo kani setup
# Use
cargo kani
# Visualize (useful after failures)
cargo kani --visualize --enable-unstable
type Number = f64;
type Vector = Vec<Complex>;
#[derive(Debug, PartialEq, Copy, Clone)]
struct Complex {
re: Number,
im: Number,
}
#[cfg(kani)]
#[kani::proof]
fn complex_numbers_identity() -> bool {
let a: f64 = kani::any();
let b: f64 = kani::any();
let x: Complex = Complex::new(a, b);
let additive_identity = Complex::new(0.0, 0.0);
x.plus(&additive_identity) == x
}
#[cfg(kani)]
#[kani::proof]
fn two_norm_squared_positive_definiteness() -> () {
let limit = 4000.0;
let a: f64 = kani::any();
let b: f64 = kani::any();
let c: f64 = kani::any();
let d: f64 = kani::any();
let e: f64 = kani::any();
let f: f64 = kani::any();
let g: f64 = kani::any();
let h: f64 = kani::any();
for i in vec![a, b, c, d, e, f, g, h] {
kani::assume(-limit < i && i < limit);
}
let zero: Complex = Complex::new(0.0, 0.0);
let x = vec![
Complex::new(a, b),
Complex::new(c, d),
Complex::new(e, f),
Complex::new(g, h),
];
if two_norm_squared(x.clone()) != 0.0 {
assert!(x != vec![zero, zero, zero, zero]);
}
}
#[cfg(kani)]
#[kani::proof]
fn two_norm_squared_nonnegative_homogeneity() -> bool {
let limit = 4000.0;
let value: f64 = kani::any();
let a: f64 = kani::any();
let b: f64 = kani::any();
let c: f64 = kani::any();
let d: f64 = kani::any();
let e: f64 = kani::any();
let f: f64 = kani::any();
let g: f64 = kani::any();
let h: f64 = kani::any();
kani::assume(-limit < value && value < limit);
for i in vec![a, b, c, d, e, f, g, h] {
kani::assume(-limit < i && i < limit);
}
let x = vec![
Complex::new(a, b),
Complex::new(c, d),
Complex::new(e, f),
Complex::new(g, h),
];
let product: Vector = x.iter()
.map(|i| i.mult_scalar(value))
.collect();
two_norm_squared(product) == value.abs() * two_norm_squared(x)
}
#[cfg(kani)]
#[kani::proof]
fn two_norm_squared_triangle_inequality() -> bool {
let limit = 4000.0;
let value: f64 = kani::any();
let a: f64 = kani::any();
let b: f64 = kani::any();
let c: f64 = kani::any();
let d: f64 = kani::any();
let e: f64 = kani::any();
let f: f64 = kani::any();
let g: f64 = kani::any();
let h: f64 = kani::any();
kani::assume(-limit < value && value < limit);
for i in vec![a, b, c, d, e, f, g, h] {
kani::assume(-limit < i && i < limit);
}
let x = vec![
Complex::new(a, b),
Complex::new(c, d),
Complex::new(e, f),
Complex::new(g, h),
];
let sum: Vector = x.iter().enumerate()
.map(|(index, element)| element.plus(&x[index]))
.collect();
two_norm_squared(sum) <= two_norm_squared(x) + two_norm_squared(x)
}
# Build Docker container
docker run -it --name notebook -p 1337:1337 ghcr.io/jhwohlgemuth/lambda
# Clone GitHub project
git clone https://github.com/jhwohlgemuth/verified-linear-algebra
cd verified-linear-algebra
# Install Kani
cargo install --locked kani-verifier
cargo kani setup
# Execute proofs
cargo kani
"Math" world
"Real" world