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:
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.
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
}
seek
outputs the proof.n
: the result is found for n = 5.2
and bigger. As n
becomes larger running becomes slow, but the result is still found for n = 7
on snark, and even n = 10
on sparrow (which has 20GB memory for the JVM).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.
We use an abstract breadth first search method for goals based on a type X
, using Monix Tasks. This depends on:
tv: Task[Vector[Task[X]]]
X => Option[Y]
spawn: Int => X => Task[Vector[Task[X]]]
for spawning new tasksAt 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.
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.
At an abstract level, instead of searching for goals, we should consider:
Iterant
along with tasks.Vector[X]
by any Monad F[X]
, with simple flows corresponding to the Id
Monad.decay
was applied with power depth
, which meant no decay at the first step.decay
has power depth + 1
; the default decay in the Monoid task was changed to 3