Documentation

LeanPool.IsoGraph.Canon.Jump

Backjumping is sound #

When the search finds a leaf whose certificate ties the incumbent's, it harvests the automorphism autoOf relating the two labellings and then backjumps: it abandons every branch between the current leaf and the deepest node the two leaves share. This file justifies that.

The longest common prefix #

theorem IsoGraph.Canon.commonPrefixFrom_ge (a b : Array ) (m fuel i : ) :
i mi commonPrefixFrom a b m fuel i
theorem IsoGraph.Canon.commonPrefixFrom_le (a b : Array ) (m fuel i : ) :
i mcommonPrefixFrom a b m fuel i m
theorem IsoGraph.Canon.commonPrefixFrom_eq (a b : Array ) (m fuel i k : ) :
i kk < commonPrefixFrom a b m fuel ia[k]! = b[k]!
theorem IsoGraph.Canon.commonPrefixFrom_ne (a b : Array ) (m fuel i : ) :
i mm i + fuelcommonPrefixFrom a b m fuel i < ma[commonPrefixFrom a b m fuel i]! b[commonPrefixFrom a b m fuel i]!
theorem IsoGraph.Canon.commonPrefix_eq {a b : Array } {k : } (h : k < commonPrefix a b) :
a[k]! = b[k]!

The paths of two leaves agree strictly before their longest common prefix.

The automorphism relating two leaves maps one branch onto the other #

theorem IsoGraph.Canon.Part.WF.lab_permArr {n : } {p : Part} (hp : WF n p) :
theorem IsoGraph.Canon.auto_path {n : } {f : Bool} {path1 path2 : Array } {ip1 ip2 : Array UInt64} {p1 p2 : Part} (h1 : Node n f path1 ip1 p1) (h2 : Node n f path2 ip2 p2) {j : } (hj1 : j < path1.size) (hj2 : j < path2.size) (hpre : List.take j path1.toList = List.take j path2.toList) :
(autoOf n p1.lab p2.lab)[path1[j]!]! = path2[j]!

The harvested automorphism follows the paths. If two nodes agree on their first j individualised vertices, the permutation autoOf builds from their labellings sends the j-th choice of one to the j-th choice of the other — because Node.pin parks both at the same position.

Ancestors #

theorem IsoGraph.Canon.Node.ancestor {n : } {f : Bool} {path : Array } {ip : Array UInt64} {p : Part} (h : Node n f path ip p) (j : ) :
j path.size∃ (iq : Array UInt64) (q : Part), Node n f (path.extract 0 j) iq q

Every prefix of a node's path is itself a node.

Backjumping is sound #

theorem IsoGraph.Canon.jump_sound {n : } {f : Bool} {path1 path2 : Array } {ip1 ip2 iq : Array UInt64} {p1 p2 q : Part} {j : } {k : List (List UInt64)} (h1 : Node n f path1 ip1 p1) (h2 : Node n f path2 ip2 p2) (hq : Node n f (path1.extract 0 j) iq q) (hcert : certOf (Graph.ofOracle n f) p1.lab = certOf (Graph.ofOracle n f) p2.lab) (hj : j commonPrefix path1 path2) (hj1 : j < path1.size) (hj2 : j < path2.size) (hreach : Reach n f (childInv (Graph.ofOracle n f) iq q path1[j]!) (child (Graph.ofOracle n f) q path1[j]!).1 k) :
Reach n f (childInv (Graph.ofOracle n f) iq q path2[j]!) (child (Graph.ofOracle n f) q path2[j]!).1 k

The key lemma behind the backjump. Two leaves with equal certificates differ by an automorphism γ. If j is the length of their common path prefix and q the depth-j node they share, then γ fixes q and sends q's child path1[j] to q's child path2[j]. So every leaf key still to be found below the first branch already occurs below the second — which is the branch depth-first search has already finished.