Skip to content

jonsterling/agda-calf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

calf: A Cost-Aware Logical Framework

The calf language is a cost-aware logical framework 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 directory. These files were generated by running:

agda --html --html-dir=html src/index.agda

You may want to start by opening 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 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.

Language Implementation

Cost Monoid Parameterization

calf is parameterized by a cost monoid (ℂ, +, zero, ≤). The formal definition, CostMonoid, is given in Algebra.Cost. 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; 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 contains commonly-used definitions.
  • Calf.CBPV defines the basic dependent Call-By-Push-Value (CBPV) language, using Agda postulates and rewrite rules.
  • Calf.Directed defines a preorder on each type, per the developments in decalf.
  • Calf.Phase defines the phase distinction of extension and intension:

The following modules are parameterized by a CostMonoid:

  • Calf.Step defines the computational effect step and the associated coherence laws via rewrite rules.

The following modules are parameterized by a ParCostMonoid:

  • Calf.Parallel defines the parallel execution operation _∥_ whose cost structure is given by the product operation of a ParCostMonoid (i.e., _⊗_).

Types

In 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:

The following modules define custom, calf-specific data types for cost analysis and are parameterized by a CostMonoid:

  • Calf.Data.IsBoundedG 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 instantiates IsBoundedG for cost bounds of the form step (F unit) c (ret triv).
  • Calf.Data.BoundedFunction defines cost-bounded functions using IsBounded.
  • Calf.Data.BigO 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.

Sequential Algorithms

  • 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

  • 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.
  • 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

First, we develop a common collection of definitions and theorems used in both sequential and parallel sorting.

  • Examples.Sorting.Comparable
    • 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
    • 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.

Here, we use cost monoid ℕ-CostMonoid, tracking the total number of sequential steps incurred.

  • Examples.Sorting.Sequential.InsertionSort
    • 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
    • Examples.Sorting.Sequential.MergeSort.Split
      • 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
      • 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

Amortized data structures, via coinduction.

Decalf

The examples introduced in decalf are included in Examples.Decalf.

We implement and analyze the basic double example.

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.
  • 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.

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

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

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