An open API service indexing awesome lists of open source software.

https://github.com/jonsterling/agda-calf

A cost-aware logical framework, embedded in Agda.
https://github.com/jonsterling/agda-calf

agda complexity cost logical-framework modal-type-theory

Last synced: 6 months ago
JSON representation

A cost-aware logical framework, embedded in Agda.

Awesome Lists containing this project

README

          

# **calf**: A Cost-Aware Logical Framework

The **calf** language is a **c**ost-**a**ware **l**ogical **f**ramework for studying quantitative aspects of functional programs.

This repository contains the Agda implementation of **calf**, as well as some case studies of varying complexity.

## HTML Browsing

The source code may be viewed interactively with (semantic) syntax highlighting in the browser using the HTML files in the [`./html`](./html) directory.
These files were generated by running:
```sh
agda --html --html-dir=html src/index.agda
```

You may want to start by opening [`html/index.html`](./html/index.html).

To view a specific module `M`, open `html/M.html` in a web browser.
For example, open [`html/Examples.Sorting.Parallel.html`](./html/Examples.Sorting.Parallel.html) to view the module `Examples.Sorting.Parallel`.

## Installation

This implementation of **calf** has been tested using:
- Agda v2.6.3, with `agda-stdlib` v2.0 experimental

Installation instructions may be found in [`INSTALL.md`](./INSTALL.md).

## Language Implementation

### Cost Monoid Parameterization

**calf** is parameterized by a *cost monoid* `(ℂ, +, zero, ≤)`.
The formal definition, `CostMonoid`, is given in [`Algebra.Cost`](./src/Algebra/Cost.agda).
The definition of a *parallel cost monoid* `(ℂ, ⊕, 𝟘, ⊗, 𝟙, ≤)` is given, as well, as `ParCostMonoid`.

Some common cost monoids and parallel cost monoids are given in [`Algebra.Cost.Instances`](./src/Algebra/Cost/Instances.agda); for example, `ℕ-CostMonoid` simply tracks sequential cost.
Note that every `ParCostMonoid` induces a `CostMonoid` via the additive substructure `(ℂ, ⊕, 𝟘, ≤)`.

### Core Language

The language itself is implemented via the following files, which are given in a dependency-respecting order.

The following modules are not parameterized:
- [`Calf.Prelude`](./src/Calf/Prelude.agda) contains commonly-used definitions.
- [`Calf.CBPV`](./src/Calf/CBPV.agda) defines the basic dependent Call-By-Push-Value (CBPV) language, using Agda `postulate`s and rewrite rules.
- [`Calf.Directed`](./src/Calf/Directed.agda) defines a preorder on each type, per the developments in [**decalf**](https://arxiv.org/abs/2307.05938).
- [`Calf.Phase`](./src/Calf/Phase.agda) defines the phase distinction of extension and intension:
- [`Calf.Phase.Core`](./src/Calf/Phase/Core.agda) postulates a proposition, `ext`, for the extensional phase.
- [`Calf.Phase.Open`](./src/Calf/Phase/Open.agda) defines the open/extensional modality `◯` for `ext`.
- [`Calf.Phase.Closed`](./src/Calf/Phase/Closed.agda) defines the closed/intensional modality `●` for `ext`.
- [`Calf.Phase.Directed`](./src/Calf/Phase/Directed.agda) postulates the **decalf** law that under `ext`, inequality coincides with equality.
- [`Calf.Phase.Noninterference`](./src/Calf/Phase/Noninterference.agda) contains theorems related to the phase distinction/noninterference.

The following modules are parameterized by a `CostMonoid`:
- [`Calf.Step`](./src/Calf/Step.agda) defines the computational effect `step` and the associated coherence laws via rewrite rules.

The following modules are parameterized by a `ParCostMonoid`:
- [`Calf.Parallel`](./src/Calf/Parallel.agda) defines the parallel execution operation `_∥_` whose cost structure is given by the product operation of a `ParCostMonoid` (i.e., `_⊗_`).

### Types

In [`src/Calf/Data`](./src/Calf/Data), we provide commonly-used data types.

The following modules are not parameterized and simply internalize the associated Agda types via the `meta⁺` primitive:
- [`Calf.Data.Bool`](./src/Calf/Data/Bool.agda)
- [`Calf.Data.Equality`](./src/Calf/Data/Equality.agda)
- [`Calf.Data.List`](./src/Calf/Data/List.agda)
- [`Calf.Data.Maybe`](./src/Calf/Data/Maybe.agda)
- [`Calf.Data.Nat`](./src/Calf/Data/Nat.agda)
- [`Calf.Data.Product`](./src/Calf/Data/Product.agda)
- [`Calf.Data.Sum`](./src/Calf/Data/Sum.agda)

The following modules define custom, **calf**-specific data types for cost analysis and are parameterized by a `CostMonoid`:
- [`Calf.Data.IsBoundedG`](./src/Calf/Data/IsBoundedG.agda) defines a generalized notion of cost bound, `IsBoundedG`, where a bound is a program of type `F unit`.
Additionally, it provides lemmas for proving the boundedness of common forms of computations.
- [`Calf.Data.IsBounded`](./src/Calf/Data/IsBounded.agda) instantiates `IsBoundedG` for cost bounds of the form `step (F unit) c (ret triv)`.
- [`Calf.Data.BoundedFunction`](./src/Calf/Data/BoundedFunction.agda) defines cost-bounded functions using `IsBounded`.
- [`Calf.Data.BigO`](./src/Calf/Types/BigO.agda) gives a definition of "big-O" asymptotic bounds via `IsBounded`.
In particular, an element of the type `given A measured-via size , f ∈𝓞(g)` (i.e., "given an input of type `A` and a size measure `size` on `A`, `f` is in `𝓞(g)`) is a lower bound on input sizes `n'` and a constant multiplier `k` along with a proof `h` that for all inputs `x` with `n' ≤ size x`, `f x` is bounded by `k` multiples of `g (size x)`, denoted `n' ≤n⇒f[n]≤ k g[n]via h`.

## Examples

We provide a variety of case studies in [`src/Examples`](./src/Examples).

### Sequential Algorithms

#### [`Examples.Id`](./src/Examples/Id.agda)
- `module Easy`
- Definition of the program `id` that trivially returns its input.
- Definition of the cost bound program `id/bound`, which here is the same as `id`.
- Theorem `id/is-bounded` showing that `id` is bounded by `id/bound`.
- Theorem `id/correct` stating the extensional correctness of `id` as a corollary of `id/is-bounded`.
- Theorem `id/asymptotic : given nat measured-via (λ n → n) , id ∈𝓞(λ n → 0)` stating that `id` is in `𝓞(0)`.
- `module Hard`
- Definition of the program `id` that reconstructs its input via induction.
- Definition of the cost bound program `id/bound`, which incurs `n` cost before returning `n`.
- Theorem `id/is-bounded` showing that `id` is bounded by `id/bound`.
- Theorem `id/correct` stating the extensional correctness of `id` as a corollary of `id/is-bounded`.
- Theorem `id/asymptotic : given nat measured-via (λ n → n) , id ∈𝓞(λ n → n)` stating that `id` is in `𝓞(n)`, where `n` is the input number.
- A proof that `Easy.id` and `Hard.id` are extensionally equivalent, `easy≡hard : ◯ (Easy.id ≡ Hard.id)`, as a corollary of the `id/correct` proofs.

### Parallel Algorithms

#### [`Examples.TreeSum`](./src/Examples/TreeSum.agda)
- Definition of the program `sum` that sums the elements of a tree, incurring unit cost when performing each addition operation.
At each node, the recursive calls are computed in parallel.
- Definition of the cost bound program `sum/bound`, which incurs `size t , depth t` cost before returning the sum of the tree via a value-level function.
- Theorem `sum/has-cost` stating that `sum` and `sum/bound` are equivalent.
- Theorem `sum/is-bounded` stating that the cost of `sum t` is bounded by `sum/bound`, as a corollary of `sum/has-cost`.

#### [`Examples.Exp2`](./src/Examples/Exp2.agda)
- `module Slow`
- Definition of the program `exp₂` that computes the exponentation of two by its input by performing two identical recursive calls.
Since two identical recursive calls are made in parallel, the work is exponential, but the span is still linear.
- Definition of the cost bound program `exp₂/bound`, incurring `2 ^ n - 1 , n` cost before returning result `2 ^ n`.
- Theorem `exp₂/is-bounded` showing that `exp₂` is bounded by `exp₂/bound`.
- Theorem `exp₂/correct` stating the extensional correctness of `exp₂` as a corollary of `exp₂/is-bounded`.
- Theorem `exp₂/asymptotic : given nat measured-via (λ n → n) , exp₂ ∈𝓞(λ n → 2 ^ n , n)` stating that `exp₂` is in `𝓞(2 ^ n , n)`.
- `module Fast`
- Definition of the program `exp₂` which computes the exponentation of two by its input via a standard recursive algorithm.
- Definition of the cost bound program `exp₂/bound`, incurring `n , n` cost before returning result `2 ^ n`.
- Theorem `exp₂/is-bounded` showing that `exp₂` is bounded by `exp₂/bound`.
- Theorem `exp₂/correct` stating the extensional correctness of `exp₂` as a corollary of `exp₂/is-bounded`.
- Theorem `exp₂/asymptotic : given nat measured-via (λ n → n) , exp₂ ∈𝓞(λ n → n , n)` stating that `exp₂` is in `𝓞(n , n)`.
- A proof that `Slow.exp₂` and `Fast.exp₂` are extensionally equivalent, `slow≡fast : ◯ (Slow.exp₂ ≡ Fast.exp₂)`.

### Hybrid Algorithms

#### [`Examples.Sorting`](./src/Examples/Sorting.agda)
First, we develop a common collection of definitions and theorems used in both sequential and parallel sorting.
- [`Examples.Sorting.Comparable`](./src/Examples/Sorting/Comparable.agda)
- Record `Comparable` describing the requirements for a type to be comparable, including `h-cost`, a hypothesis that each comparison is bounded by unit cost.
This serves as the cost model for sorting.
- [`Examples.Sorting.Core`](./src/Examples/Sorting/Core.agda)
- Predicates for correctness of sorting, based on `Sorted` and the permutation relation `↭` from `agda-stdlib`.
The predicate `IsSort sort` states that `sort` is a correct sorting algorithm.
- Theorem `IsSort⇒≡`, which states that any two correct sorting algorithms are extensionally equivalent.

##### [`Examples.Sorting.Sequential`](./src/Examples/Sorting/Sequential.agda)
Here, we use cost monoid `ℕ-CostMonoid`, tracking the total number of sequential steps incurred.

- [`Examples.Sorting.Sequential.InsertionSort`](./src/Examples/Sorting/Sequential/InsertionSort.agda)
- Definition of the program `sort` implementing insertion sort.
- Theorem `sort/correct : IsSort sort` verifying the correctness of `sort`.
- Theorem `sort≤sort/cost/closed` stating that the cost of `sort l` is bounded by `sort/cost/closed l = length l ²`.
- Theorem `sort/asymptotic : given (list A) measured-via length , sort ∈𝓞(λ n → n ²)` stating that `sort` is in `𝓞(n ²)`, where `n` is the length of the input list.
- [`Examples.Sorting.Sequential.MergeSort`](./src/Examples/Sorting/Sequential/MergeSort.agda)
- [`Examples.Sorting.Sequential.MergeSort.Split`](./src/Examples/Sorting/Sequential/MergeSort/Split.agda)
- Definition of the program `split`, which splits a list in halves.
- Theorem `split/correct` verifying correctness properties of `split`.
- Theorem `split≤split/cost` stating that the cost of `split l` is bounded by `zero`, since splitting a list into halves requires no comparisons.
- [`Examples.Sorting.Sequential.MergeSort.Merge`](./src/Examples/Sorting/Sequential/MergeSort/Merge.agda)
- Definition of the program `merge`, which merges a pair of sorted lists.
- Theorem `merge/correct` verifying correctness properties of `merge`.
- Theorem `merge≤merge/cost/closed` stating that the cost of `merge (l₁ , l₂)` is bounded by `length l₁ + length l₂`.
- Definition of the program `sort` implementing merge sort.
- Theorem `sort/correct : IsSort sort` verifying the correctness of `sort`.
- Theorem `sort≤sort/cost/closed` stating that the cost of `sort l` is bounded by `sort/cost/closed l = ⌈log₂ length l ⌉ * length l`.
- Theorem `sort/asymptotic : given (list A) measured-via length , sort ∈𝓞(λ n → n * ⌈log₂ n ⌉)` stating that `sort` is in `𝓞(n * ⌈log₂ n ⌉)`, where `n` is the length of the input list.

Theorem `isort≡msort : ◯ (ISort.sort ≡ MSort.sort)` states that `InsertionSort.sort` and `MergeSort.sort` are extensionally equivalent.

### Data Structures

#### Amortized

##### [`Examples.Amortized`](./src/Examples/Amortized.agda)

Amortized data structures, [via coinduction](https://drops.dagstuhl.de/opus/volltexte/2023/18820).

- [`Examples.Amortized.Simple`](./src/Examples/Amortized/Simple.agda) provides an amortized implementation of a simple amortized stream abstract data type.
- [`Examples.Amortized.Queue`](./src/Examples/Amortized/Queue.agda) provides an implementation of [amortized queues](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)#Amortized_queue).
- [`Examples.Amortized.DynamicArray`](./src/Examples/Amortized/DynamicArray.agda) provides an implementation of dynamically-growing arrays.

### Decalf

The examples introduced in **decalf** are included in [`Examples.Decalf`](./src/Examples/Decalf.agda).

#### [`Examples.Decalf.Basic`](./src/Examples/Decalf/Basic.agda)

We implement and analyze the basic `double` example.

#### [`Examples.Decalf.Nondeterminism`](./src/Examples/Decalf/Nondeterminism.agda)

We introduce the `branch` and `fail` primitives for nondeterminism and give the corresponding examples.

- `module QuickSort` includes the nondeterministic quicksort algorithm using primitives from [`Examples.Sorting.Sequential.Core`](./src/Examples/Sorting/Sequential.agda).
- `module Lookup` includes the list lookup function that fails on out-of-bounds indices.
- `module Pervasive` includes a simple example of pervasive (non-benign) nondeterminism.

#### [`Examples.Decalf.ProbabilisticChoice`](./src/Examples/Decalf/ProbabilisticChoice.agda)

We introduce the probabilistic `flip` primitive and give the corresponding example, showing how the cost of `sublist` is bounded by the `binomial` distribution.

#### [`Examples.Decalf.GlobalState`](./src/Examples/Decalf/GlobalState.agda)

We introduce the `get` and `set` primitives for global state and show a simple imperative program whose cost bound involves `get` and `set`.

#### [`Examples.Decalf.HigherOrderFunction`](./src/Examples/Decalf/HigherOrderFunction.agda)

We define the `twice` and `map` higher-order functions and analyze them under assumptions about their input costs.