Zulip Chat Archive

Stream: Equational

Topic: A final end-to-end theorem in Lean


Joachim Breitner (Oct 17 2024 at 08:24):

Overnight, I thought a bit more about what an end-to-end theorem in lean could look like. Put differently, what is a “minimal” set of definitions and theorems that one would have to manually inspect and which convey the end result of this project, all checked in lean

This would be my proposal. For defs with :=, the actual value on the right-hand side is not necessary for the skeptical human to check, and would likely be filled by a macro or command.

def n  : Nat := 
/-- all our laws, in one definition -/
def law  : (Fin n)  NatMagmaLaw := 
/-- a proof that this contains all laws we claim to look at -/
theorem law_complete :  (l : NatMagmaLaw), l.countOps  4,  i, law i = l
/-- the final matrix -/
def implicationGraph : (i j : Fin n)  Bool := 
/-- and correctness thereof -/
theorem implicationGraph_correct :  (i j : Fin n),
  implicationGraph i j = true  (law i).implies (law j)

Notes:

  • I am phrasing law and implementationGraph as functions, not lists or matrices or so. That’s sufficient to state these theorems in a clear and declarative way. Very likely one has to find suitable data structures, define them, and then access these in law and implementationGraph.
  • Filling these definitions naively, e.g. by pure enumeration, wouldn’t be too hard, I assume, but likely be too large for lean to handle. So this needs serious proof engineering. In particular, I expect that law should be backed by a balanced binary search tree that the kernel can index into somewhat quickly (I have code for that somewhere). And not sure about the graph, maybe actually a bitmap encoded in a large Nat?
  • For the graph, maybe it makes sense to re-implement the closure code in a verified and kernel-reduction-friendly way. So one step would be to define the (partial) implies and not-implies graphs, and then prove that the final graph is the result of taking the closure using a verified algorithm, rather than proving correctness of each entry in the final graph.
  • Arguably the above is trivial to satisfy if one uses the axioms of classical logic in the definitions. So maybe one should also add a check that implementationGraph doesn’t use such axioms, and hence is actually computable.
  • law_complete cannot be proven like this, we want to restrict it to those laws with variables in order (or up-toprenaming)

If one would want to build that, and want to build it even before proving everything, then one either has to complicate the statements somehow (excluding certain pairs from the correctness theorem). Or introduce an axiom for “conjectured implication” and build this infrastructure as if we are already done, and when we are done then that axiom will be unused.

Notification Bot (Oct 17 2024 at 08:24):

A message was moved here from #Equational > Thoughts and impressions thread by Joachim Breitner.

Joachim Breitner (Oct 17 2024 at 16:25):

Playing around in that direction. That’s a good start:

def laws : RArray Law.NatMagmaLaw := defineLaws%
example : laws[1000] = Law1001 := rfl

Shreyas Srinivas (Oct 17 2024 at 16:31):

Ah I see, this is a theorem that we have proved all the theorems

Notification Bot (Oct 17 2024 at 16:50):

6 messages were moved from this topic to #Equational > non-computable non-non-computables? by Joachim Breitner.

Joachim Breitner (Oct 17 2024 at 17:19):

Little experiments in that direction in https://github.com/teorth/equational_theories/pull/618
This defines a single definition that contains all laws, and a binary search function to find the index of a law therein.

But what’s missing for laws_complete is a verified(!) generator of laws up to a certain size, which is doable, but not trivial. Parking this for today.

Shreyas Srinivas (Oct 17 2024 at 17:23):

There is a script from which our current set of equations were defined. It is in python and I think we also have an extraction script from lean's equations list to json

Joachim Breitner (Oct 17 2024 at 17:26):

I know, but thats not verified. How do we know that we didn't miss a certain law? (Unlikely, but if we want an end-to-end proof of our end result, that is one necessary part.)

Andrés Goens (Oct 17 2024 at 17:43):

oh, that's really cool! I've been knid of slacking off integrating duality into the transitive closure computation, so I feel reassured you think it makes sense to re-implement it in a verified and kernel-reduction-friendly way (so that effort wasn't wasted :grinning: )

Andrés Goens (Oct 17 2024 at 17:49):

Maybe it would make sense to split up the proof of implicationGraph_correct into two parts, one that says "it suffices to prove these implications/anti implications" because of transitivity, duality, and a second part that just supplies the necessary proofs. That would also help with the issue of making this work before everything's integrated (because we can prove the "it suffices" part already without having the explicit proofs for every implication/anti-implication, and that's likely the one that's going to be more optimizable)

Amir Livne Bar-on (Oct 17 2024 at 19:33):

I've been away from chat, but worked on and off on converting the code generating laws to Lean.

It's now in a working state, at
https://github.com/amirlb/equational_theories/blob/generate-laws-lean/scripts/generate_laws.lean

Amir Livne Bar-on (Oct 17 2024 at 19:35):

I'm not fluent enough in Lean yet to prove it covers all laws up to equivalence

Joachim Breitner (Oct 17 2024 at 19:51):

sorry for possibly duplicating work, I'm almost through with my generator.

Amir Livne Bar-on (Oct 17 2024 at 19:54):

It's not that much code anyway... So the important thing is to structure it so it's easy to prove properties about it

Vlad Tsyrklevich (Oct 17 2024 at 20:06):

Andrés Goens said:

oh, that's really cool! I've been knid of slacking off integrating duality into the transitive closure computation, so I feel reassured you think it makes sense to re-implement it in a verified and kernel-reduction-friendly way (so that effort wasn't wasted :grinning: )

I believe duality is already integrated as of 76ace3ce93a728b536252d206bb75c9b71b4d421 unless you're referring to something else?

Joachim Breitner (Oct 17 2024 at 20:17):

Joachim Breitner said:

sorry for possibly duplicating work, I'm almost through with my generator.

I take the almost back, it seems tricky to recognize/generate laws up to permutation of the variables efficiently.

Cody Roux (Oct 17 2024 at 20:23):

I bet this is a well-known problem, but seems hard to google

Joachim Breitner (Oct 17 2024 at 20:52):

Ok, I got a generator that finds all equations, does not generate extra ones, but doesn’t try all permutations of variables. It’s not verified yet, but this looks promising:

/-- info: true -/
#guard_msgs in
#eval (List.range 5).all fun i => testLaws i fun l =>
  laws[findMagmaLaw l] = l

Amir Livne Bar-on (Oct 17 2024 at 22:26):

Very nice!

Andrés Goens (Oct 18 2024 at 05:44):

Vlad Tsyrklevich said:

Andrés Goens said:

oh, that's really cool! I've been knid of slacking off integrating duality into the transitive closure computation, so I feel reassured you think it makes sense to re-implement it in a verified and kernel-reduction-friendly way (so that effort wasn't wasted :grinning: )

I believe duality is already integrated as of 76ace3ce93a728b536252d206bb75c9b71b4d421 unless you're referring to something else?

right, sorry, I should have been more precise here. That piece of code is great for keeping score, but it's just taking the duals out of some json file:

let dualityRelation  DualityRelation.ofFile "data/duals.json"

I mean formally using the duality definition and metatheorem (from equational#285) when computing the closure, as part of an end-to-end theorem in Lean

Andrés Goens (Oct 18 2024 at 06:11):

Joachim Breitner said:

Ok, I got a generator that finds all equations, does not generate extra ones, but doesn’t try all permutations of variables. It’s not verified yet, but this looks promising:

/-- info: true -/
#guard_msgs in
#eval (List.range 5).all fun i => testLaws i fun l =>
  laws[findMagmaLaw l] = l

cool! by the way, formalizing this also probably requires/is closely related to equational#148 and equational#184, formalizing the counts

Joachim Breitner (Oct 18 2024 at 12:27):

Proved

theorem laws_complete :   l : Law.MagmaLaw Nat, l.forks  4  l.is_canonical   (i : Nat), laws[i] = l

This is using native_decide, but still a very convincing proof, I’d say.

If one defines a suitable equivalence on MagmaLaw (up to relabeling of variables, reorienting the equation), then one could even prove the slightly more high-level

theorem laws_complete :   l : Law.MagmaLaw Nat, l.forks  4   l', l.equiv l'   (i : Nat), laws[i] = l'

and take l.is_canonical out of the “trusted spec base”. Looks like we don’t have that equivalence definition yet, though.

Amir Livne Bar-on (Oct 18 2024 at 13:33):

We do have logical equivalence, which is weaker. And looks even more convincing to me: it doesn't matter if we missed a few equations if we provably covered all equivalence classes.

Joachim Breitner (Oct 18 2024 at 13:36):

True, so we’d just™ have to prove that relabeling or symmetry preserves logical equivalence.

Amir Livne Bar-on (Oct 18 2024 at 13:59):

That is simple enough for even me to prove :p (and might even be exactly MagmaLaw.satisfies_map_equiv). Syntactic equivalence sounds like a useful property to have though so probably worth defining anyway.

Joachim Breitner (Oct 18 2024 at 20:53):

In https://github.com/teorth/equational_theories/pull/636 I am experimenting with representing the graph of proven implications in a single def, in a verified way. I think it built locally, but seems to bring the GitHub runner to its knees. This definitely needs more tuning. It needs more work to actually scale to the full graph, though.

This doesn't take the closure yet. For that I am wondering if there is a way to precalculate some data so that the actual proof doesn't have to calculate the closure, just check it. For example if for every nodes i, j in the closure it would also store the number of steps it takes from i to j, and also what the next node on that path is, then checking that for correctness should be linear. Maybe that's noticably better than running the full closure algorithm.

Amir Livne Bar-on (Oct 19 2024 at 06:10):

If it was a simple graph, it would be simple, right? You could prove that for one vertex you have both a forward and a backwards path to every other vertex

Amir Livne Bar-on (Oct 19 2024 at 06:24):

Actually I never saw the structure of the (non-)implications graph written down, so for completeness:

  • There are two nodes for each equation, EqA and EqA'
  • There is a node for each "Facts" statement, MagmaN
  • For each statement EqA_implies_EqB two edges: EqB -> EqA and EqB' -> EqA'
  • For each statement Facts [A, ...] [B, ...] edges are added EqA -> MagmaN and MagmaN -> EqB'

Then a path EqA -> ... -> EqB indicates an implication EqB => EqA and a path EqA -> ... -> EqB' indicates a non-implication not (EqA => EqB).

Joachim Breitner (Oct 19 2024 at 11:16):

Joachim Breitner said:

I think it built locally, but seems to bring the GitHub runner to its knees. This definitely needs more tuning. It needs more work to actually scale to the full graph, though.

Interesting, the file finishes fine in VSCode, but lake build takes a long time. :eyes:

Even worse: lake build sits there for minutes, and if I stop it and re-run it, it considers the build done. That looks like a bug somewhere…
Ah, it seems that writing the C file with the large data structure is what keeps Lean overly busy (and which doesn’t happen in vscode). I think that’s what @Henrik Böving observed before. And after Ctrl-C there is an empty .c file and lake is happy to use that :-(
But I fail to reproduce this behavior in a small example.

Joachim Breitner (Oct 19 2024 at 11:45):

Hmm, using large bitvectors encoded as Nat seems to hit limitations in lean:

  • Compiling is super slow because Nat.repr goes via toDigits which goes via List Char, instead of mutably filling a String. Reported as https://github.com/leanprover/lean4/issues/5771.
  • The generated C code runs lean_cstr_to_nat when initializing, so presumably (didn’t measure) this increases the startup time of any program importing such a module.

But maybe a single bitvector for the whole graph isn’t needed, and maybe something like Array Nat (i.e. adjacency bitvectors) would work better.

Amir Livne Bar-on (Oct 19 2024 at 12:24):

fwiw the closure code uses arrays of bitvectors, where each bitvector is an array of UInt64

Joachim Breitner (Oct 19 2024 at 14:41):

That is a good choice for native evaluation. Maybe I should first aim at proving the final theorem using native_decide and the existing code, and defer (or give up on) trying to avoid native evaluation.

Mario Carneiro (Oct 19 2024 at 14:47):

Joachim Breitner said:

Hmm, using large bitvectors encoded as Nat seems to hit limitations in lean:

  • Compiling is super slow because Nat.repr goes via toDigits which goes via List Char, instead of mutably filling a String. Reported as https://github.com/leanprover/lean4/issues/5771.
  • The generated C code runs lean_cstr_to_nat when initializing, so presumably (didn’t measure) this increases the startup time of any program importing such a module.

But maybe a single bitvector for the whole graph isn’t needed, and maybe something like Array Nat (i.e. adjacency bitvectors) would work better.

Why are you using Nat.repr?

Mario Carneiro (Oct 19 2024 at 14:48):

I thought the idea was that you read a data file, construct a Nat in memory at elaboration time and insert it as a nat literal into the environment

Mario Carneiro (Oct 19 2024 at 14:50):

you should never need to go via decimal, you really shouldn't because base conversion of huge numbers is incredibly expensive

Mario Carneiro (Oct 19 2024 at 17:43):

Here's an alternative proof structure, which perhaps sidesteps the questions about computability:

import equational_theories.Preorder
import equational_theories.MagmaOp

open Law renaming NatMagmaLaw -> Law

def Fact' (as bs : List Law) :=  a  as,  b  bs, ¬a  b

inductive Record where
  | implies : Law  Law  Record
  | notImplies : Law  Law  Record
  | fact : List Law  List Law  Record
  | dual : Law  Law  Record

def Record.WF : Record  Prop
  | .implies a b => a  b
  | .notImplies a b => ¬a  b
  | .fact as bs =>  a  as,  b  bs, ¬a  b
  | .dual a b => b = a.dual

def IsDual (l : List Record) (a a' : Law) : Prop :=
  .dual a a'  l  .dual a' a  l

inductive Closure (l : List Record) : Law  Law  Bool  Prop
  | refl {a} : Closure l a a true
  | imp {a b} : .implies a b  l  Closure l a b true
  | notimp {a b} : .notImplies a b  l  Closure l a b false
  | fact {as bs a b} : .fact as bs  l  a  as  b  bs  Closure l a b false
  | trans11 {a b c} : Closure l a b true  Closure l b c true  Closure l a c true
  | trans10 {a b c} : Closure l a b true  Closure l a c false  Closure l b c false
  | trans01 {a b c} : Closure l a c false  Closure l b c true  Closure l a b false
  | dual {a b a' b' val} : IsDual l a a'  IsDual l b b'  Closure l a b val  Closure l a' b' val

theorem Closure.wf {l} (h :  r  l, r.WF) (a b : Law) {val} :
    Closure l a b val  (a  b  val) := sorry

def allLaws : List Law := [
  -- explicit list
]

def allRecords : List Record := [
  -- explicit list
]

theorem allRecords_correct :  r  allRecords, r.WF := sorry

theorem correct (eqA eqB : Law) {b} :
    Closure allRecords eqA eqB b  (eqA  eqB  b) := sorry

theorem complete (eqA eqB : Law) : eqA  allLaws  eqB  allLaws 
     b, Closure allRecords eqA eqB b := sorry

Joachim Breitner (Oct 19 2024 at 18:14):

Nice. Does this scale, usings lists etc, given thousands of entries?

Mario Carneiro (Oct 19 2024 at 18:15):

not really, at least the way lists in lean currently work. But you can have a metaprogram that generates trees instead of lists and/or uses sub-definitions and I'm pretty sure you can push it through the kernel that way

Joachim Breitner (Oct 19 2024 at 18:15):

I am computing the large nat using an elaborator (so I assume reading from a file wouldn't be different here), and use it to define a top level Nat. But then lean wants to write ot to the c file, and itself uses Nat.repr in the process

Notification Bot (Oct 19 2024 at 18:20):

3 messages were moved here from #Equational > non-computable non-non-computables? by Mario Carneiro.

Mario Carneiro (Oct 19 2024 at 18:21):

@Joachim Breitner is it okay to just put noncomputable on the definition?

Joachim Breitner (Oct 19 2024 at 18:22):

Mario Carneiro said:

not really, at least the way lists in lean currently work. But you can have a metaprogram that generates trees instead of lists and/or uses sub-definitions and I'm pretty sure you can push it through the kernel that way

Ok, sounds like I wasn't completely on the wrong track on my Branch, with a tree-shaped proof of well-formedness of the main data structure. Fun puzzle, although it would be nice if one didn't have to puzzle to make such medium sized things work.

Mario Carneiro (Oct 19 2024 at 18:22):

yes, lean falls over on big proofs for distressingly small values of "big"

Joachim Breitner (Oct 19 2024 at 18:22):

Mario Carneiro said:

Joachim Breitner is it okay to just put noncomputable on the definition?

Possibly, but I assumed I can only do that once I no longer use native_decide

Mario Carneiro (Oct 19 2024 at 18:23):

IIRC there is some way to force definitions to be interpreted, but I forget what it is. Maybe just disable the compiler with an option?

Mario Carneiro (Oct 19 2024 at 18:27):

I would be interested to know what kind of algorithms exist for proving completeness without needing to enumerate all n^2 cases

Mario Carneiro (Oct 19 2024 at 18:28):

it feels like you should be able to construct a hasse diagram and extract a bunch of facts implicitly from that

Mario Carneiro (Oct 19 2024 at 18:40):

actually I think that's exactly the answer: a hasse diagram presentation where every edge is a proved non-implication is already complete, so the proof is just to construct a hasse diagram presentation and check that every law is somewhere in the diagram

Amir Livne Bar-on (Oct 19 2024 at 19:03):

A small search found an anti-chain of size 200, so we need at least 200^2 negative edges in the graph

Mario Carneiro (Oct 19 2024 at 19:06):

It's possible to prove those 200^2 negative edges using only 200 models though in the best case

Mario Carneiro (Oct 19 2024 at 19:07):

in other words, we have to consider the extended graph you mentioned that also has nodes for Facts statements

Mario Carneiro (Oct 19 2024 at 19:08):

@Joachim Breitner I'm looking at your RArray structure, very nice. One thing which I think would improve it is to use a slightly different version of the get function:

/-- The crucial operation, written with very little abstractional overhead -/
noncomputable def RArray.get : RArray α  Nat  α :=
  RArray.rec (fun x _ => x) (fun p _ _ l r n => (Nat.ble p n).rec (l n) (r (Nat.sub n p)))

In other words, the data stored at nodes is not the index of the middle point in the array but rather the size of the left subtree. This has the effect that many operations on it become O(log n) instead of O(n)

Mario Carneiro (Oct 19 2024 at 19:10):

get might also get faster, although it's unlikely to matter at these sizes, because you are comparing smaller numbers

Joachim Breitner (Oct 19 2024 at 19:11):

They are not O(log(n))? Then I didn't do what I intended. I stored the index there to avoid the subtraction in get

Mario Carneiro (Oct 19 2024 at 19:12):

get is O(log n) but insertion isn't

Joachim Breitner (Oct 19 2024 at 19:12):

Oh, you man other operators than get? This was designed to support exactly get.

Mario Carneiro (Oct 19 2024 at 19:12):

In particular, I think you can prove the theorem in LawsComplete by just building up the array instead of looking individuals up in a static array

Joachim Breitner (Oct 19 2024 at 19:12):

I.e. for use cases where you prepare static data at elab time, and at kernel reduction time you just index into. Hence also the garbage value when indexing out of bounds.

Mario Carneiro (Oct 19 2024 at 19:12):

because you know it is sorted in advance

Joachim Breitner (Oct 19 2024 at 19:13):

For example the denotation function in a reflection proof could use this instead of a list

Mario Carneiro (Oct 19 2024 at 19:14):

Joachim Breitner said:

They are not O(log(n))? Then I didn't do what I intended. I stored the index there to avoid the subtraction in get

I'm not sure what the alternative you are considering here is

Joachim Breitner (Oct 19 2024 at 19:14):

Right. When writing the code I wasn't sure if my generation function would end up producing the same order as Terry's script (and I'm not sure it does), hence this approach for now.

Joachim Breitner (Oct 19 2024 at 19:15):

Mario Carneiro said:

Joachim Breitner said:

They are not O(log(n))? Then I didn't do what I intended. I stored the index there to avoid the subtraction in get

I'm not sure what the alternative you are considering here is

Nevermind, ignore that message, I thought you said that my get wasn't O(log n).

Joachim Breitner (Oct 19 2024 at 19:16):

Do you want to take over working on the two end-to-end theorems? You seem to have been thinking about it already a lot, and I shouldn't get too distracted from core work anyways ?

Mario Carneiro (Oct 19 2024 at 19:17):

Joachim Breitner said:

Right. When writing the code I wasn't sure if my generation function would end up producing the same order as Terry's script (and I'm not sure it does), hence this approach for now.

But you never sort the list? It seems to just put Law(i+1) at position i in the list

Mario Carneiro (Oct 19 2024 at 19:18):

but findMagmaLaw exploits that it is sorted according to your sort order

Mario Carneiro (Oct 19 2024 at 19:18):

so I think this is a proof that the laws are in fact sorted

Mario Carneiro (Oct 19 2024 at 19:19):

Joachim Breitner said:

Do you want to take over working on the two end-to-end theorems? You seem to have been thinking about it already a lot, and I shouldn't get too distracted from core work anyways ?

lol, I need to be reviewing a paper, I'm intensely procrastinating right now

Joachim Breitner (Oct 19 2024 at 19:19):

(deleted)

Mario Carneiro (Oct 19 2024 at 19:19):

I don't think we even need to prove it is transitively reduced

Joachim Breitner (Oct 19 2024 at 19:20):

Sorry, I think I said nonsense, but you answer too quickly

Joachim Breitner (Oct 19 2024 at 19:21):

(nonsense because you also need to demonstrate non-implications between nodes that are unrelated in the graph)

Joachim Breitner (Oct 19 2024 at 19:22):

Right, with transitive reduction I meant we can remove some implied edges, not that it has to be fully reduced.

Joachim Breitner (Oct 19 2024 at 19:24):

Mario Carneiro said:

so I think this is a proof that the laws are in fact sorted

Yes, Terry's list is sorted wrt to some order that I had to find out. I don't know if my generator produces them in that order, hence generate-and-lookup.

Mario Carneiro (Oct 19 2024 at 19:25):

So you are saying that Law.MagmaLaw.comp is actually terry's order?

Mario Carneiro (Oct 19 2024 at 19:25):

but you don't know how to generate laws in that order

Joachim Breitner (Oct 19 2024 at 19:31):

Yes, its his order (so that binary search works), and I didn't attempt to recreate his order so far.

Terence Tao (Oct 19 2024 at 20:02):

By the way, the script was written by @Amir Livne Bar-on , back in the prehistory of the project on Sep 26 (when it was in a different repository).

Notification Bot (Oct 19 2024 at 20:57):

36 messages were moved from this topic to #Equational > proving a hasse diagrams to be complete by Joachim Breitner.

Amir Livne Bar-on (Oct 20 2024 at 05:00):

If we change the type of the checker from Law -> Bool to Law -> Monoid it can also be a generator. Then we can test and tweak the order if we want it to fit the list.

Amir Livne Bar-on (Oct 20 2024 at 09:27):

This is easier said than done. The nice way I managed to put it uses higher-order types, and I can't see how to make the induction on iterFreeMagmas (nee testFreeMagmas) work correctly.
https://github.com/amirlb/equational_theories/blob/laws_iterator/equational_theories/Equations/LawsComplete.lean#L176

The ordering is still not exactly the same, but we can probably shuffle some indices to make it output in the same order.

Joachim Breitner (Oct 20 2024 at 10:43):

Is it worth the bother? Generate-and-lookup seems fine to me, and the speed is acceptable.

Amir Livne Bar-on (Oct 20 2024 at 10:52):

Probably not worth it. Especially since (as I realized after I got the list) it doesn't generate any law more than twice

Mario Carneiro (Oct 20 2024 at 12:35):

well, I wanted to improve on the native_decide part

Mario Carneiro (Oct 20 2024 at 12:36):

@Amir Livne Bar-on do you have code?

Amir Livne Bar-on (Oct 20 2024 at 13:07):

Yes, the link above is a file that generates lists and checks properties with the same code. It works, only one theorem is broken.

But if we go for complicated type-classes here, we may as well use ForIn which has syntax and support for propositions.

Mario Carneiro (Oct 20 2024 at 13:42):

oh, I missed that that was a link to your fork

Joachim Breitner (Nov 03 2024 at 00:03):

Joachim Breitner said:

Is it worth the bother? Generate-and-lookup seems fine to me, and the speed is acceptable.

Maybe worth the bother if we want to get rid of native_decide, and need to make it go a lot faster.

In https://github.com/teorth/equational_theories/pull/780 I saw how far I get with using some tricks to make well-founded recursion go well and with reducing some overhead (typeclasses, Monad), but it is still far too slow.

I expect if we refactor the code to use a generator (as @Amir Livne Bar-on started to do), and either make it generate them in the right order, or go trough a lookup table for the permuation (which can be calculated at elaboration time), then maybe it’s feasible.

@Amir Livne Bar-on , List.append is likely far too slow here, due to the quadratic runtime; you probably need at least difference lists as the structure to collect the results, or maybe implement the generator as left-folds (resp. StateM) and use List.cons to add new entries to the front.

Or, don’t even generate the list, and stick closer to the current code, but keep a counter that’s passed to the predicate and then we can use that counter to avoid the (likely expensive) findMagmaLaw search.

Mario Carneiro (Nov 03 2024 at 00:11):

Can someone remind me where to find the definition of the magma enumeration ordering?

Terence Tao (Nov 03 2024 at 00:32):

Here's the lean implementation: https://leanprover.zulipchat.com/#narrow/channel/458659-Equational/topic/Reconstructing.20the.204694.20expressions.20from.20the.20ground.20up.2E/near/478350231 . I should put the ordering in the blueprint at some point.

Mario Carneiro (Nov 03 2024 at 00:37):

@Terence Tao said:

  • The ordering on laws with a fixed length on both the left-hand side and right-hand side is a bit hard to explain (recursive lexicographic). At this point it is easier just to point to the python code than to try to describe it properly here.

What python code?

Vlad Tsyrklevich (Nov 03 2024 at 00:38):

scripts/generate_eqs_list.py

Amir Livne Bar-on (Nov 03 2024 at 05:44):

The Cn+1C_{n+1} factor hints at a simple explanation: consider the tree corresponding to lhs * rhs, first order by that, and then lexicographically

Joachim Breitner (Nov 03 2024 at 10:12):

JFTR, Mario solved the issue, as mentioned over at #Equational > LawsComplete by proof term generation

Andrés Goens (Dec 04 2024 at 13:40):

So I was looking back a bit at this and it seems we have solved the enumeration issue (laws complete), but we still have no proof that includes the implications/refutations. I'm not sure how we'd even want to state that theorem, saying that for every pair of laws l1l2l_1 l_2 we either have a proof of l1l2l_1 \vdash l_2 or a refutation.

The best way I can think of is defining the set of implications as an adjacency matrix implications (similar to laws in LawsComplete) and proving something like laws[i] ≤ laws[j] ↔ implications[i][j] = true. Does someone have a better idea? Is it worth adding this as an issue? (I would be interested in contributing to this, although I don't know how much time I will have for it)

Andrés Goens (Dec 04 2024 at 13:44):

having said that, this array is probably a bit too large to have in memory (and be efficient), so maybe we want to phrase this for the transitive closure of a sparse matrix only

Joachim Breitner (Dec 04 2024 at 13:44):

That is roughly the theorem, but the harder question is the necessary proof engineering that we don't generate 4000² individual proofs.

I would expect something like

  • a reasonable efficient representation of all explicitly proven facts (impliciations and counter-examples), maybe transitively reduced to keep it small
  • a proof that for each entry in that data structure we really have a theorem
  • a proof that the the closure of this data structure cover the whole graph.

Ideally the third point can be proven in a way that does not require going through the whole graph. I believe Mario did think about this quite a bit.

Andrés Goens (Dec 04 2024 at 14:10):

Right, so you think that just storing the explicitly proven facts like we're currently doing (with the @equational_result tag) is going to be too inefficient?

Joachim Breitner (Dec 04 2024 at 14:12):

I think that depends on the algorithm used for the third step, and whether that will involve kernel-computation.

Andrés Goens (Dec 04 2024 at 14:13):

rg "@\[equational_result" | wc -l
12452

I just checked roughly and it seems it's about 12k explicitly proven facts. That might indeed be a bit too large for putting them all together one proof term, so we should think about how to split them up

Andrés Goens (Dec 04 2024 at 14:14):

(this set is probably not minimal, but ballpark it still sounds like too much)

Joachim Breitner (Dec 04 2024 at 14:14):

I don’t know how useful it is to split them up if you end up having to put them together for the third proof step?

Andrés Goens (Dec 04 2024 at 14:15):

Joachim Breitner said:

I don’t know how useful it is to split them up if you end up having to put them together for the third proof step?

but for the third proof step it doesn't have to actually look at the proof term, just its type, right?

Joachim Breitner (Dec 04 2024 at 14:15):

Right, but the type would still mention a data structure that contains 12k entries, woudn’t it

Joachim Breitner (Dec 04 2024 at 14:16):

I am not primarily worried about a def with a careful chosen data structure with 12k elements, and then a proof that the entries are correct that is essentially 12k times “see theorem foo” (step 2). It’s step 3 that worries me.

And as step3 likely influence which data structure to use here, I’m hesitant to worry about step 2 before that is determined.

Andrés Goens (Dec 04 2024 at 14:18):

hmm, interesting :thinking: yeah, I think I see the problem

Joachim Breitner (Dec 04 2024 at 14:19):

I think #Equational > proving a hasse diagrams to be complete is the thread where approaches to step 3 were discussed

Mario Carneiro (Dec 04 2024 at 14:54):

12k explicit facts doesn't sound that bad, that's only about 3 times larger than the list of equations itself

Mario Carneiro (Dec 04 2024 at 14:55):

that's a very sparse graph

Joachim Breitner (Dec 04 2024 at 14:57):

Note that each counter-example fact can represent a large amount of actual edges, but still.

Mario Carneiro (Dec 04 2024 at 14:57):

It might help for some folks to try to do an analysis of the full hasse diagram to see whether there are any interesting structures we can exploit (eg. does it have many connected components? is it almost linear? etc)

Bruno Le Floch (Dec 04 2024 at 14:58):

Is there some interest in porting find_equation_id.py from python to Lean? I can deal with the complexities of indices but I don't understand yet how to get started: for instance I don't understand the relation between equation in Equations/Eqns1_999.lean and EquationN in files like ParseImplications.lean. Is there some documentation of that somewhere?

Vlad Tsyrklevich (Dec 04 2024 at 14:58):

Yes, there are 10k implications, and ~1.3k refutations but they represent around ~500k edges. That ~500k number is not all necessary for completeness though

Mario Carneiro (Dec 04 2024 at 14:58):

I'm surprised there are so few refutations

Vlad Tsyrklevich (Dec 04 2024 at 14:59):

Most of the refutations are resolved by just looking at 3x3 magmas.

Mario Carneiro (Dec 04 2024 at 14:59):

The refutations are also grouped, right? i.e. they are more like "fact nodes" saying that Ai -/> Bj for all i,j in some lists A, B

Vlad Tsyrklevich (Dec 04 2024 at 15:00):

Yes, that's what I mean by 1.3k theorems but 500k edges.

Mario Carneiro (Dec 04 2024 at 15:00):

right, this is definitely going to be exploited by the proof

Vlad Tsyrklevich (Dec 04 2024 at 15:00):

500k is the sum of the 1.3k Satisfying*Refuting

Mario Carneiro (Dec 04 2024 at 15:00):

there will be no "multiply all these terms together" proof

Mario Carneiro (Dec 04 2024 at 15:01):

at least not if I have anything to say about it :)

Mario Carneiro (Dec 04 2024 at 15:02):

how many edges are there if we count a fact as having A+B edges?

Vlad Tsyrklevich (Dec 04 2024 at 15:02):

Mario Carneiro said:

It might help for some folks to try to do an analysis of the full hasse diagram to see whether there are any interesting structures we can exploit (eg. does it have many connected components? is it almost linear? etc)

Strongly connected components is already know, there are 1,415 equivalence classes, the largest is of size 1,496, but 1,145/1,415 are of size 1

Mario Carneiro (Dec 04 2024 at 15:03):

oh well that makes things easier

Vlad Tsyrklevich (Dec 04 2024 at 15:03):

Yes, the SCC graph is ~10x smaller than the original

Vlad Tsyrklevich (Dec 04 2024 at 15:04):

And there are only ~4.8k edges in the transitive reduction between equivalence classes

Vlad Tsyrklevich (Dec 04 2024 at 15:04):

The rest are establishing equivalence classes.

Mario Carneiro (Dec 04 2024 at 15:04):

or does it? If there are a huge number of isolated components (once you take off the top and bottom, I assume) don't we have to prove all the n^2 non-implications?

Vlad Tsyrklevich (Dec 04 2024 at 15:05):

Don't you just have to prove the most general refutations? I mean perhaps that entails some level of graph search as well, but I'm not sure that it's n^2

Mario Carneiro (Dec 04 2024 at 15:05):

Let's suppose that step 1 of the proof is to separate out all the size 1 components and handle them specially. What is the proof strategy?

Joachim Breitner (Dec 04 2024 at 15:05):

Just checking: Are you talking about the same thing if you say equivalence classes resp. connected components?

Mario Carneiro (Dec 04 2024 at 15:06):

I am picturing a diagram with 0 <= x_i <= 1 and all x_i incomparable

Vlad Tsyrklevich (Dec 04 2024 at 15:06):

Why are size 1 components special? I think you treat all SCCs equivalently, and then prove that if it's complete for SCCs it's true for the graph.

Mario Carneiro (Dec 04 2024 at 15:07):

They aren't, but I want to exploit known structure in the graph to make the proof smaller

Joachim Breitner (Dec 04 2024 at 15:07):

I think Mario is already one step ahead, and looks at a graph where equivalent laws are already represented by a single node, with the top and bottom law cut off, and now wants to know the connected components of this graph

Mario Carneiro (Dec 04 2024 at 15:07):

something like "80% of the graph is trivial" definitely qualifies

Vlad Tsyrklevich (Dec 04 2024 at 15:08):

How can there be connected components, it's already a condensed graph

Vlad Tsyrklevich (Dec 04 2024 at 15:09):

Ah, perhaps I'm confusing terminology with connected components and strongly connected?

Mario Carneiro (Dec 04 2024 at 15:10):

Yes, Joachim is correct. The connected components I'm talking about are in the undirected version of the transitive reduction graph, after collapsing equivalent nodes and deleting the top and bottom

Vlad Tsyrklevich (Dec 04 2024 at 15:11):

Well, take what I said above to be about strongly connected components in the base graph, not this model.

Mario Carneiro (Dec 04 2024 at 15:12):

SCCs would be single nodes in what I said. How many nodes are there in the graph after reducing SCCs?

Mario Carneiro (Dec 04 2024 at 15:13):

oh wait you answered that already (1415)

Joachim Breitner (Dec 04 2024 at 15:14):

In addition to knowing how many such components we have it’s also relevant to know, for each of them, their size, the number of least elements and the number of greatest elements (because we’ll need counter-examples between each greatest and each least element, unless I’m mistaken).

Mario Carneiro (Dec 04 2024 at 15:15):

yeah knowing the actual numbers helps a lot with determining how much engineering is overengineering

Mario Carneiro (Dec 04 2024 at 15:16):

e.g. no need to deploy Tarjan's SCC algorithm if it's all singletons

Mario Carneiro (Dec 04 2024 at 15:17):

but I think SCCs are essentially an independent part of the proof from the hasse diagram structure

Vlad Tsyrklevich (Dec 04 2024 at 15:25):

For least and greatest, I assume you just meant inbound edges to Equation 1 and outbound edges of Equation 2, or do you mean this recursively, since some connected components are large?

Joachim Breitner (Dec 04 2024 at 15:26):

I assume that you remove Eq1 and Eq2 first (“deleting top and bottom”), and then the graph falls apart into connected components. Of these components, it’s relevant to know how many least and greatest there are.

Ah, but you are right: these are indeed the edges from and to Eq1 and Eq2 in the transitively reduced graph.

Vlad Tsyrklevich (Dec 04 2024 at 15:27):

Here's graphiti showing outbound edges from Eq2. Open the JS console and it shows that there are 46 edges.

Vlad Tsyrklevich (Dec 04 2024 at 15:27):

For Equation 1 it's 57 inbounds.

Joachim Breitner (Dec 04 2024 at 15:28):

Thanks! That also puts upper bounds on the number of connected components (after deleting Eq 1 and Eq2)

Vlad Tsyrklevich (Dec 04 2024 at 15:38):

Looking at the size of the components outwards from Eq2, they have this number of elements:

Joachim Breitner (Dec 04 2024 at 15:39):

Nice! Are Eq4 and Eq5 are in the same component of size 693?

Vlad Tsyrklevich (Dec 04 2024 at 15:40):

No, they are dual.

Joachim Breitner (Dec 04 2024 at 15:40):

And the small ones have size 2 because you are including Eq1 in the count? Or are they all equivalence classes consisting of two laws?

Andrés Goens (Dec 04 2024 at 15:41):

so this list is already just taking a single representative of every scc?

Vlad Tsyrklevich (Dec 04 2024 at 15:41):

No I'm not including Eq1, for example, here is 1076

Vlad Tsyrklevich (Dec 04 2024 at 15:42):

Andrés Goens said:

so this list is already just taking a single representative of every scc?

Yes.

Vlad Tsyrklevich (Dec 04 2024 at 15:43):

I just noticed the list has sporadic equations (or order >4) included but there are only a few of them so it shouldn't affect the numbers much.

Joachim Breitner (Dec 04 2024 at 15:43):

Looking at 271 here it looks like repeating the process of cutting off top or bottom elements and recursing might handle a good part of the graph.

Andrés Goens (Dec 04 2024 at 15:46):

I guess also generating and proving the 1415 theorems that describe the scc's is a pretty good idea in any case already, right?

Andrés Goens (Dec 04 2024 at 15:46):

independent of how we split the collapsed graph

Andrés Goens (Dec 04 2024 at 15:46):

we'll need to collapse it

Vlad Tsyrklevich (Dec 04 2024 at 15:48):

(There are only 270 SCCs of size >1)

Mario Carneiro (Dec 04 2024 at 15:48):

Vlad Tsyrklevich said:

No, they are dual.

Oh, duality is also going to be super helpful here. Can we pair up the connected components into dual pairs which have the exact same structure?

Mario Carneiro (Dec 04 2024 at 15:49):

I guess Eq41 has to be self-dual

Vlad Tsyrklevich (Dec 04 2024 at 15:49):

41 is, but 895 is not.

Vlad Tsyrklevich (Dec 04 2024 at 15:49):

(895 is equivalent to it's dual, hence you don't see it.)

Mario Carneiro (Dec 04 2024 at 15:51):

I mean the cluster should be self-dual, i.e. the image of the dual map on the 895 cluster is itself

Mario Carneiro (Dec 04 2024 at 15:51):

Are there really no size 1 components?

Vlad Tsyrklevich (Dec 04 2024 at 15:53):

I don't think there can be, any >1 variable equation implies its one-variable version.

Mario Carneiro (Dec 04 2024 at 15:55):

but the 1 variable version could be top or bottom, no?

Mario Carneiro (Dec 04 2024 at 15:55):

i.e. that argument proves there are two nodes but not that neither of them is extremal (or that they are inequivalent)

Mario Carneiro (Dec 04 2024 at 15:56):

there might be a general argument for why reducing the variable count always results in an inequivalent equation though

Vlad Tsyrklevich (Dec 04 2024 at 15:57):

Mario Carneiro said:

but the 1 variable version could be top or bottom, no?

If I understand what you mean correctly, it has to be top, e.g. most general. It's only restricting elements related to the diagonal.

Mario Carneiro (Dec 04 2024 at 15:58):

does graphiti have an option for collapsing equivalent nodes?

Vlad Tsyrklevich (Dec 04 2024 at 15:58):

It already collapses them

Mario Carneiro (Dec 04 2024 at 15:59):

what does rounded corners mean?

Vlad Tsyrklevich (Dec 04 2024 at 15:59):

It means it's an SCC of size >1.

Alex Meiburg (Dec 04 2024 at 15:59):

It might also be possible to make the SCC computation faster by adding just a _few_ extra theorems. Like for the Eq2 equivalence class, adding a theorem for EqX -> Eq2 directly (instead of relying on dynamically finding a path through other theorems) means that we can verify everything in the SCC by just checking those O(n) extra theorems.

Alex Meiburg (Dec 04 2024 at 16:00):

While creating all 4000^2 theorems is obviously too much, adding a theorem for "X implies 2" for each X that does seems totally fine as overehead/optimization

Mario Carneiro (Dec 04 2024 at 16:01):

It's not clear why we would need that

Mario Carneiro (Dec 04 2024 at 16:01):

We need to prove SCCs, not non-SCCs

Vlad Tsyrklevich (Dec 04 2024 at 16:01):

Mario Carneiro said:

there might be a general argument for why reducing the variable count always results in an inequivalent equation though

You're right that my original argument missed this, and the argument as to why this is the case is that that setting all 0s along the diagonal always satisfies the one-variable equation, and then setting all the other elements to 1 should cause the multi-variable version to fail.

EDIT: I suppose this actually isn't true for x = (xx)(yy), though it is for other equations it's equivalent to.

Mario Carneiro (Dec 04 2024 at 16:01):

non-SCCs will be implied from non-implications during the hasse diagram part of the proof

Vlad Tsyrklevich (Dec 04 2024 at 16:20):

Got lost in something else, here's the list of component sizes looking outwards from Eq2 paired by dual:

Amir Livne Bar-on (Dec 04 2024 at 19:51):

Some stats that I didn't see mentioned in the thread already:

  • The 1415 components are comprised of 67 self-dual components and 674 pairs of dual components
  • The number of transitively-reduced implications in the Hesse diagram is 4824, or 2467 up to duality
  • There are 14522 irreducible non-implications needed to prove that the diagram is fully-specified, or 7286 up to duality

Vlad Tsyrklevich (Dec 04 2024 at 19:56):

Oh, the last number is nice. I have a PR that adds dashboard stats that computes and publishes various facts about the graph, I should add that as well. By what algorithm do you compute the minimum required non-implications?

Amir Livne Bar-on (Dec 04 2024 at 20:03):

Nothing too smart, went over all the non-implications and checked if each can be derived directly from an implication + a stronger non-implication. Takes a few seconds in python.

Mario Carneiro (Dec 04 2024 at 20:06):

Where is the actual (explicit) implication data? The graphiti json doesn't have the implications themselves, only the induced implications on SCCs

Amir Livne Bar-on (Dec 04 2024 at 20:06):

lake exe extract_implications outcomes

Joachim Breitner (Dec 04 2024 at 20:12):

Amir Livne Bar-on said:

  • There are 14522 irreducible non-implications needed to prove that the diagram is fully-specified, or 7286 up to duality

How do you obtain this number? Using some algorithm that takes a Hesse diagram and gives you the least number of anti-implications you have to provide to prove that the diagram is fully specified?

Mario Carneiro (Dec 04 2024 at 20:20):

Amir Livne Bar-on said:

lake exe extract_implications outcomes

This seems to require the whole lean project to be built, I was hoping for a data dump file

Vlad Tsyrklevich (Dec 04 2024 at 20:23):

Do you know if you have a particular data format you want?

Vlad Tsyrklevich (Dec 04 2024 at 20:24):

e.g. extract_implications versus extract_implications outcomes etc.

Mario Carneiro (Dec 04 2024 at 20:27):

lake exe extract_implications doesn't run at all, it says

uncaught exception: object file '././.lake/build/lib/equational_theories.olean' of module equational_theories does not exist

Vlad Tsyrklevich (Dec 04 2024 at 20:27):

(deleted)

Vlad Tsyrklevich (Dec 04 2024 at 20:27):

Oops, different exception

Mario Carneiro (Dec 04 2024 at 20:27):

by which I inferred that it needs the project to be built

Mario Carneiro (Dec 04 2024 at 20:30):

Vlad Tsyrklevich said:

Do you know if you have a particular data format you want?

implications in json would be nice

Mario Carneiro (Dec 04 2024 at 20:31):

I have managed to get stuff out of outcomes.png, which I recall being a recommendation before

Vlad Tsyrklevich (Dec 04 2024 at 20:32):

https://tsyrklevi.ch/general.json.gz

Vlad Tsyrklevich (Dec 04 2024 at 20:32):

Once equational#973 lands (and assuming github pages accepts it), this should just be available on the dashboard going forward

Vlad Tsyrklevich (Dec 04 2024 at 20:33):

That's the outcome of just plain extract_implications

Amir Livne Bar-on (Dec 04 2024 at 20:41):

Joachim Breitner said:

Amir Livne Bar-on said:

  • There are 14522 irreducible non-implications needed to prove that the diagram is fully-specified, or 7286 up to duality

How do you obtain this number? Using some algorithm that takes a Hesse diagram and gives you the least number of anti-implications you have to provide to prove that the diagram is fully specified?

This number doesn't include the fact that the edges of the diagram are strict implications, so the full number is actually 19346. Here's the main chunk of code I used to count these:

non_implications = {(i, j) for i in range(M) for j in range(M) if j not in implications[i] and i not in implications[j]}
simple_non_implications = {
    (i, j) for i, j in non_implications
    if all((i, k) not in non_implications for k in implications_rev[j] if k != j)
    and all((k, j) not in non_implications for k in implications[i] if k != i)
}

Mario Carneiro (Dec 04 2024 at 20:43):

I get 10675 explicit implications and 587150 nonimplications from that json @Vlad Tsyrklevich , is this correct?

Mario Carneiro (Dec 04 2024 at 20:43):

that seems higher than the numbers we've been talking about

Mario Carneiro (Dec 04 2024 at 20:44):

the nonimplications are unfortunately inflated because I don't think the "facts" are being tracked anywhere

Vlad Tsyrklevich (Dec 04 2024 at 20:44):

10,657 I think. You can check https://teorth.github.io/equational_theories/dashboard/

Mario Carneiro (Dec 04 2024 at 20:44):

that's weird, it's not a typo

Amir Livne Bar-on (Dec 04 2024 at 20:44):

This is for the equations, before reducing to SCCs. So there are more edges.

Vlad Tsyrklevich (Dec 04 2024 at 20:45):

For raw facts, look here https://tsyrklevi.ch/general_raw_full_entries.json

Vlad Tsyrklevich (Dec 04 2024 at 20:45):

Mario Carneiro said:

that's weird, it's not a typo

Ah, that may just be duplicates

Amir Livne Bar-on (Dec 04 2024 at 20:45):

Your numbers look slightly higher, so maybe equations beyond 4694 are included?

Vlad Tsyrklevich (Dec 04 2024 at 20:46):

Oh yes, also sporadic equations. The two great confounders.

Vlad Tsyrklevich (Dec 04 2024 at 20:46):

There are 10 duplicates on main, the rest are sporadic I think.

Mario Carneiro (Dec 04 2024 at 20:55):

There seem to be no implications in general_raw_full_entries.json with finite = true

Vlad Tsyrklevich (Dec 04 2024 at 20:55):

You need a different output for that, want the finite one?

Mario Carneiro (Dec 04 2024 at 20:56):

not really, just checking

Vlad Tsyrklevich (Dec 04 2024 at 20:56):

The output is per graph, either general or finite.

Mario Carneiro (Dec 04 2024 at 20:56):

I would have thought the raw data just contains all the lean-proven theorems in whatever disposition

Mario Carneiro (Dec 04 2024 at 20:57):

I assume the finite facts are included though

Vlad Tsyrklevich (Dec 04 2024 at 20:58):

Yes.

Mario Carneiro (Dec 04 2024 at 20:58):

there is only one :)

Mario Carneiro (Dec 04 2024 at 20:59):

wait nevermind, I can't count

Amir Livne Bar-on (Dec 04 2024 at 21:03):

By the way, I checked and even if we remove the components for equations 1 and 2 there is a single connected component.

What would a cut get us, if we find one in an inner part of the graph? It'll probably make the proof simpler to write, but will take longer to verify?

Vlad Tsyrklevich (Dec 04 2024 at 21:04):

How is that possible? See https://teorth.github.io/equational_theories/graphiti/?render=true&neighborhood_of_distance=3&neighborhood_of=1076

Vlad Tsyrklevich (Dec 04 2024 at 21:04):

Ah wait, I'm only looking up from Eq2, not down from Eq1, yes that still makes sense.

Mario Carneiro (Dec 04 2024 at 21:05):

Horizontal cuts are better than vertical, but that doesn't help us much in this very flat graph

Amir Livne Bar-on (Dec 04 2024 at 21:05):

If anyone wants to play with the SCC-reduced graph, I'm attaching my data here (script, textual output, sqlite)

analyze_comps.py
comps_graph.db
comps_graph.txt

Vlad Tsyrklevich (Dec 05 2024 at 07:11):

Just an FYI, that a number of the stats we've discussed here and the exported data is now available on https://teorth.github.io/equational_theories/dashboard/

Martin Dvořák (Dec 06 2024 at 10:39):

Sorry; I don't have anything to contribute.
I just want to say that I am really happy that people work on this.

Mario Carneiro (Dec 06 2024 at 15:25):

I spent a while burning brain energy on this hasse diagram problem, and my conclusion is that my previous suggestion of using some kind of sweep across the poset doesn't work, or not well enough. The new plan is much simpler:

We want to prove every pair A,BLawsA,B\in Laws is represented in the poset (either via implication or nonimplication). Call this property Dec(AB)\operatorname{Dec}(A\le B). We generalize to the predicate Dec(ST)\operatorname{Dec}(S\le T) where S,TLawsS,T\subseteq Laws, which asserts that Dec(AB)\operatorname{Dec}(A\le B) for all AS,BTA\in S, B\in T. We now have the following rules:

  • Split left: If S1S2=SS_1\cup S_2=S, then Dec(S1T)Dec(S2T)Dec(ST)\operatorname{Dec}(S_1\le T)\land \operatorname{Dec}(S_2\le T)\to \operatorname{Dec}(S\le T)
  • Split right: If T1T2=TT_1\cup T_2=T, then Dec(ST1)Dec(ST2)Dec(ST)\operatorname{Dec}(S\le T_1)\land \operatorname{Dec}(S\le T_2)\to \operatorname{Dec}(S\le T)
  • Nonimplies: If M=min(S)M=min(S) is the set of minimal elements of S and N=max(T)N=max(T) similarly, and A≰BA\not\le B for all AM,BNA\in M,B\in N, then Dec(ST)\operatorname{Dec}(S\le T).

The main strategy then for certificate generation is to pick a (preferably explicit) nonimplication A≰BA\not\le B for which A B|{\uparrow}A|\ |{\downarrow}B| is as large as possible (where A{\uparrow}A is the set of elements of SS above AA and likewise for B{\downarrow}B), grow it if possible to M N|{\uparrow}M|\ |{\downarrow}N| while maintaining the property that every element of MM is not below every element of NN, then use the rules:

  • split left with S=M(SM)S={\uparrow}M\cup (S\setminus {\uparrow}M)
    • split right with T=N(TN)T={\downarrow}N\cup (T\setminus {\downarrow}N)
      • nonimplies on M,NM, N
      • recurse to prove Dec(M(TN))\operatorname{Dec}({\uparrow}M\le (T\setminus {\downarrow}N)).
    • recurse to prove Dec((SM)T)\operatorname{Dec}((S\setminus {\uparrow}M)\le T).

Using only splitting, we get a quadratic size proof. But the nonimplies proof is proportional only to M N|{\uparrow}M|\ |{\downarrow}N|, and MM and NN will generally be very small here (usually 1) so this knocks out entire contiguous rectangles from the implication matrix at a time.

If the procedure fails to make progress, then there must not be any nonimplications remaining. Proof strategy TODO for this case


Last updated: May 02 2025 at 03:31 UTC