Prover Components and Identities in a Monoid

Mathematical goal

Given a multiplication operation with left and right identities $e_l$ and $e_r$, it is a basic but non-trivial result that $e_l = e_r$. The proof is:

  1. from $e_l * x = x$ deduce $e_l * e_r = e_r$.
  2. from $x * e_r = x$ deduce $e_l * e_r = e_l$.
  3. (Lemma) by symmetry of equality, from (2) deduce $e_l = e_l * e_r$.
  4. (Theorem) by transitivity of equality, from (3) and (1) deduce $e_l = e_r$.

Deduction principles

The full proof has very low weight to be discovered directly. Finding the proof illustrates, and tests, some key principles.

An earlier attempt, that sort of succeeded but took a couple of hours, was based on slowly tuning and using sampling. Subsequently, we used a hand-run version of the final method. Now all this has been encapsulated in code.

This is purely a forward search, though crucially with lemma discovery along the way.

Script and results

Here is the ammonite script that finds a proof:

val tv = new TermEvolver(lambdaWeight = 0.0, piWeight = 0.0)

def seek(n: Double = 6) = {
  val s = theoremSearchTask(dist1, tv, math.pow(10.0, -n), 3.minutes, eqM(l)(r), decay = 10)
  val f = s.runAsync
  f.foreach{
    case Some(t) => println(s"Found Proof ${t.fansi}\nfor Theorem ${t.typ.fansi}")
    case None => println("could not find theorem")
  }
  f
}

What is a lemma?

A proposition is a type with a witness. We can associate to it two generation probabilities, p of the statement and q of proofs - obtained using the term to type map. We use a parsimomy based entropy, h(p, q) = -p/ (q * log(q)) and give weights to lemmas based on this.

We deduce consequences of lemmas, allocating resources depending on the weight.

Breadth first search

We use an abstract breadth first search method for goals based on a type X, using Monix Tasks. This depends on:

At each stage, first we look if the goal is attained across all the tasks. If not, we spawn tasks from each task. If the set of spawned tasks in empty, we terminate with None. Otherwise we recurse, but incrementing depth.

Lemmas and theorems

Our first task is built using a TermEvolver to recursively define a probability distribution, and then evolving by Truncate.task with a cutoff. Lemmas are evaluated as above.

For subsequent steps, we generate using the derivative component with a term evolver, with cutoff based on the weight of the lemma, and higher cutoffs at greater depth.

Further variations

At an abstract level, instead of searching for goals, we should consider:

Remarks

Updates