Documentation

Complexity.Computability.Circuit.Frontier.Network

Constraint networks and their branching programs #

A constraint network is a multigraph whose edges carry values in an alphabet U and whose vertices impose local constraints: the constraint at v sees only the values on the edges at v and the inputs read at v. Each input is read at no more than one vertex. An input is accepted when some assignment of values to the edges satisfies every constraint.

Networks are a semantics-free form of computation. Nothing in them is directed: a gate z = g(x, y) becomes the relation {(x, y, z) | z = g(x, y)} between three edge values, and the network never needs to invert g.

The separator lemma (Network.Satisfies.splice). Cut the vertices into a set L and its complement. If two satisfying assignments, for inputs x and y, agree on the edge cut of L, then taking the first assignment near L and the second away from it satisfies the network for the input that reads like x at L and like y elsewhere. Once the values on the cut are fixed, the two sides of the network are independent.

The branching program of a layout (Network.branchingProgram). Process the vertices in the order of a layout. Layer t of the program is the frontier after the first t vertices, the edge cut between them and the rest, and its states are the values on the frontier. Step t processes the vertex v numbered t and reads the inputs read at v; its edges join the values of an assignment on the frontier before v to its values on the frontier after v when the constraint at v holds. One last step reads the inputs that the network does not read, so the program reads every input exactly once.

Exactness (Network.accepted_branchingProgram). The frontier values of a satisfying assignment form an accepting path. Conversely, along an accepting path the value on an edge persists while the edge stays on the frontier, so the assignments witnessing the steps agree on every edge and glue into a satisfying assignment (Network.exists_satisfies_of_isAcceptingPath). If satisfying assignments are unique, the program is unambiguous.

Counting edges. An edge of the program at step t is determined by the values on two consecutive frontiers and the inputs read at the vertex processed in between. So a layout of width w in a network of degree at most d, reading at most m inputs per vertex, gives a program with at most |U| ^ (w + d + m) edges at each of its first |V| steps. Its final step has at most |U| ^ #(unread inputs) edges (Network.size_branchingProgram_le), and the rectangle bound for read-once programs bounds the accepted set (Network.ncard_accepted_le); peeling bounds the bias of a set on it (Network.abs_signedCount_accepted_le).

Sweeps with extra messages (Network.frontierSweep). Any values val x on the edges, not necessarily satisfying, together with an extra message at each time, form a sweep as soon as the inputs sending the same messages splice. This is how a global register, such as a ledger, travels along a layout next to the values on the frontier.

Main definitions #

Main results #

structure Cslib.Circuits.Frontier.Network (U : Type u_1) (ι : Type u_2) (V : Type u_3) (E : Type u_4) extends Cslib.Circuits.Frontier.Multigraph V E :
Type (max (max (max u_1 u_2) u_3) u_4)

A constraint network over the alphabet U with inputs indexed by ι: a multigraph on vertices V and edges E, the vertex at which each input is read (if any), and a constraint at each vertex that sees only the values on the edges at that vertex and the inputs read there.

  • src : E → V
  • tgt : E → V
  • site : ι → Option V

    The vertex at which each input is read, if it is read at all.

  • Constraint : V → (ι → U) → (E → U) → Prop

    The constraint at a vertex, on the input and the values on the edges.

  • constraint_congr (v : V) (x x' : ι → U) (α α' : E → U) : (∀ (i : ι), self.site i = some v → x i = x' i) → (∀ (e : E), self.src e = v ∨ self.tgt e = v → α e = α' e) → self.Constraint v x α → self.Constraint v x' α'

    Constraints are local: the constraint at v sees only the inputs read at v and the values on the edges incident to v.

Instances For
    def Cslib.Circuits.Frontier.Network.Satisfies {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (x : ι → U) (α : E → U) :

    An assignment of values to the edges satisfies every constraint on the input x.

    Instances For
      def Cslib.Circuits.Frontier.Network.accepted {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) :
      Set (ι → U)

      The accepted inputs: those with a satisfying assignment.

      Instances For
        def Cslib.Circuits.Frontier.Network.readAt {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (v : V) :
        Set ι

        The inputs read at a vertex.

        Instances For
          def Cslib.Circuits.Frontier.Network.readIn {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (L : Set V) :
          Set ι

          The inputs read at the vertices of L.

          Instances For
            def Cslib.Circuits.Frontier.Network.read {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) :
            Set ι

            The inputs read by the network.

            Instances For
              @[simp]
              theorem Cslib.Circuits.Frontier.Network.readIn_empty {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) :
              theorem Cslib.Circuits.Frontier.Network.readIn_insert_subset {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (v : V) (L : Set V) :
              N.readIn (insert v L) ⊆ N.readIn L ∪ N.readAt v

              The inputs read by a set with one more vertex.

              theorem Cslib.Circuits.Frontier.Network.Satisfies.splice {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} {N : Network U ι V E} {L : Set V} {x y z : ι → U} {α β : E → U} (hx : N.Satisfies x α) (hy : N.Satisfies y β) (hcut : Set.EqOn α β (N.edgeCut L)) (hzx : Set.EqOn z x (N.readIn L)) (hzy : Set.EqOn z y (N.readIn L)ᶜ) :
              ∃ (γ : E → U), N.Satisfies z γ ∧ Set.EqOn γ α (N.touching L) ∧ Set.EqOn γ β (N.touching L)ᶜ

              The separator lemma. Let α satisfy the network on the input x and β on the input y, and suppose that they agree on the edge cut of L. Then the network is satisfied on any input z that agrees with x on the inputs read in L and with y on the others, by an assignment that agrees with α on the edges touching L and with β on the remaining edges.

              theorem Cslib.Circuits.Frontier.Network.dependsOn_read {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) :
              DependsOn (fun (x : ι → U) => x ∈ N.accepted) N.read

              Acceptance depends only on the inputs that the network reads.

              Frontier values and revealed inputs along a layout #

              noncomputable def Cslib.Circuits.Frontier.Network.frontierValues {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) (t : ℕ) (α : E → U) :
              E → Option U

              The values of an assignment on the frontier at time t, and nothing elsewhere.

              Instances For
                theorem Cslib.Circuits.Frontier.Network.frontierValues_of_mem {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) {t : ℕ} {α : E → U} {e : E} (he : e ∈ N.frontier π t) :
                N.frontierValues π t α e = some (α e)
                theorem Cslib.Circuits.Frontier.Network.frontierValues_of_notMem {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) {t : ℕ} {α : E → U} {e : E} (he : e ∉ N.frontier π t) :
                N.frontierValues π t α e = none
                theorem Cslib.Circuits.Frontier.Network.frontierValues_eq_iff {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) {t : ℕ} {α β : E → U} :
                N.frontierValues π t α = N.frontierValues π t β ↔ Set.EqOn α β (N.frontier π t)
                noncomputable def Cslib.Circuits.Frontier.Network.revealedBy {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) (t : ℕ) :
                Set ι

                The inputs revealed by time t: those read by the first t vertices, and after the last vertex, all inputs, including those the network does not read.

                Instances For
                  theorem Cslib.Circuits.Frontier.Network.revealedBy_mono {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) :
                  theorem Cslib.Circuits.Frontier.Network.revealedBy_succ_diff {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) {t : ℕ} (ht : t < Nat.card V) :
                  N.revealedBy π (t + 1) \ N.revealedBy π t = N.readAt ((Equiv.symm π) ⟨t, ht⟩)

                  The inputs revealed while processing the vertex numbered t are those read at that vertex.

                  theorem Cslib.Circuits.Frontier.Network.revealedBy_succ_diff_subset {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) {t : ℕ} (ht : t < Nat.card V) :
                  N.revealedBy π (t + 1) \ N.revealedBy π t ⊆ N.readAt ((Equiv.symm π) ⟨t, ht⟩)

                  The inputs revealed while processing the vertex numbered t are read at that vertex.

                  theorem Cslib.Circuits.Frontier.Network.revealedBy_card_succ_diff {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) :

                  After the last vertex, the inputs revealed are those the network does not read.

                  theorem Cslib.Circuits.Frontier.Network.exists_ncard_readIn_initial_eq {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) [Finite ι] (π : Layout V) (hm : ∀ (v : V), (N.readAt v).ncard ≤ 1) {k : ℕ} (hk : k ≤ N.read.ncard) :
                  ∃ t ≤ Nat.card V, (N.readIn (π.initial t)).ncard = k

                  Reads grow one at a time. If each vertex reads at most one input, then along every layout, for every k up to the number of inputs read, the first t vertices read exactly k inputs for some t, by the discrete intermediate value theorem.

                  The branching program of a layout #

                  noncomputable def Cslib.Circuits.Frontier.Network.branchingProgram {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) :
                  ReadOnceNBP ι U (E → Option U)

                  The branching program of a layout. Its layers are the frontiers of the layout, and its states are the values on a frontier. Step t < |V| processes the vertex v numbered t and reads the inputs read at v: an edge joins the values of an assignment α on the frontier before v to its values on the frontier after v when the constraint at v holds for α. The last step reads the inputs that the network does not read.

                  Instances For
                    theorem Cslib.Circuits.Frontier.Network.branchingProgram_edge_iff {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) {t : ℕ} {m m' : E → Option U} {a : ↑(N.revealedBy π (t + 1) \ N.revealedBy π t) → U} :
                    (N.branchingProgram π).Edge t m a m' ↔ ∃ (α : E → U) (x : ι → U), m = N.frontierValues π t α ∧ m' = N.frontierValues π (t + 1) α ∧ (N.revealedBy π (t + 1) \ N.revealedBy π t).domRestrict x = a ∧ ∀ (ht : t < Nat.card V), N.Constraint ((Equiv.symm π) ⟨t, ht⟩) x α
                    theorem Cslib.Circuits.Frontier.Network.newly_branchingProgram {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) {t : ℕ} (ht : t < Nat.card V) :

                    The step processing the vertex numbered t reads the inputs read at that vertex.

                    theorem Cslib.Circuits.Frontier.Network.newly_branchingProgram_card {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) :

                    The last step reads the inputs that the network does not read.

                    Exactness #

                    theorem Cslib.Circuits.Frontier.Network.isAcceptingPath_frontierValues {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) {x : ι → U} {α : E → U} (hα : N.Satisfies x α) :
                    (N.branchingProgram π).IsAcceptingPath x fun (t : ℕ) => N.frontierValues π t α

                    The frontier values of a satisfying assignment form an accepting path.

                    theorem Cslib.Circuits.Frontier.Network.exists_satisfies_of_isAcceptingPath {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) {x : ι → U} {γ : ℕ → E → Option U} (hγ : (N.branchingProgram π).IsAcceptingPath x γ) :
                    ∃ (α : E → U), N.Satisfies x α ∧ ∀ t ≤ Nat.card V + 1, γ t = N.frontierValues π t α

                    Gluing. An accepting path of the branching program lists the frontier values of a satisfying assignment. An edge is on the frontier strictly after its earlier end and up to its later end, and along the path it keeps the value given to it by the step that processes its earlier end. Reading each edge off that step gives an assignment that agrees with the witness of every step on the edges at its vertex, so it satisfies every constraint.

                    theorem Cslib.Circuits.Frontier.Network.accepted_branchingProgram {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) :

                    Exactness. The branching program of a layout accepts exactly the accepted inputs of the network.

                    theorem Cslib.Circuits.Frontier.Network.unambiguous_branchingProgram {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) (h : ∀ (x : ι → U) (α β : E → U), N.Satisfies x α → N.Satisfies x β → α = β) :

                    If every input has at most one satisfying assignment, the branching program of every layout is unambiguous: an accepting path lists the frontier values of a satisfying assignment.

                    Counting edges #

                    theorem Cslib.Circuits.Frontier.Network.ncard_edges_le {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) [Finite ι] [Finite U] [Finite E] {t : ℕ} (ht : t < Nat.card V) :
                    ((N.branchingProgram π).edges t).ncard ≤ Nat.card U ^ ((N.frontier π t ∪ N.frontier π (t + 1)).ncard + (N.readAt ((Equiv.symm π) ⟨t, ht⟩)).ncard)

                    Edges by width. At a step that processes a vertex, an edge of the program is determined by the values of an assignment on the two frontiers and the inputs read at the vertex.

                    theorem Cslib.Circuits.Frontier.Network.ncard_edges_last_le {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) [Finite ι] [Finite U] :

                    At the last step both frontiers are empty, so an edge of the program is determined by the unread inputs.

                    theorem Cslib.Circuits.Frontier.Network.ncard_image_path_le {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) [Finite U] [Finite E] (π : Layout V) {t : ℕ} (ht : t ≤ Nat.card V) :
                    ((fun (x : ι → U) => (N.branchingProgram π).path x t) '' N.accepted).ncard ≤ Nat.card U ^ (N.frontier π t).ncard

                    The states of the branching program of a layout. At a layer t ≤ |V|, the chosen accepting paths of the accepted inputs pass through at most |U| ^ |frontier t| states: each is the list of the values of a satisfying assignment on the frontier.

                    theorem Cslib.Circuits.Frontier.Network.size_branchingProgram_le {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) [Finite ι] [Finite U] [Nonempty U] [Finite E] {w d m : ℕ} (hw : N.width π ≤ w) (hd : N.MaxDegreeLE d) (hm : ∀ (v : V), (N.readAt v).ncard ≤ m) :

                    The size of the program. If a layout has width at most w, and every vertex has degree at most d and reads at most m inputs, then the branching program has at most |V| · |U| ^ (w + d + m) + |U| ^ #(unread inputs) edges.

                    theorem Cslib.Circuits.Frontier.Network.ncard_accepted_le {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) [Finite ι] [Finite U] [Nonempty U] [Finite E] {K : ℕ} (hfree : RectangleFree N.accepted K) {w d m : ℕ} (hw : N.width π ≤ w) (hd : N.MaxDegreeLE d) (hm : ∀ (v : V), (N.readAt v).ncard ≤ m) :
                    N.accepted.ncard ≤ (K - 1) ^ 2 * (Nat.card V * Nat.card U ^ (w + d + m) + Nat.card U ^ N.readᶜ.ncard)

                    The frontier counting lemma for networks. Let the accepted inputs of a network be K-rectangle-free. If a layout has width at most w, and every vertex has degree at most d and reads at most m inputs, then

                    |accepted| ≤ (K - 1) ^ 2 · (|V| · |U| ^ (w + d + m) + |U| ^ #(unread inputs)).

                    This is the rectangle bound for the branching program of the layout.

                    theorem Cslib.Circuits.Frontier.Network.abs_signedCount_accepted_le {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) [Finite ι] [Finite U] [Nonempty U] [Finite E] (hunique : ∀ (x : ι → U) (α β : E → U), N.Satisfies x α → N.Satisfies x β → α = β) {S : Set (ι → U)} {K : ℕ} {b : ℝ} (hb : 0 ≤ b) (hS : RectangleBias S K b) {w d m : ℕ} (hw : N.width π ≤ w) (hd : N.MaxDegreeLE d) (hm : ∀ (v : V), (N.readAt v).ncard ≤ m) :
                    |signedCount S N.accepted| ≤ 2 * b * ↑N.accepted.ncard + ↑((K - 1) ^ 2 * (Nat.card V * Nat.card U ^ (w + d + m) + Nat.card U ^ N.readᶜ.ncard))

                    Peeling for networks. Let S have bias at most b ≥ 0 on rectangles with both sides of size at least K, and let every input have at most one satisfying assignment. If a layout has width at most w, and every vertex has degree at most d and reads at most m inputs, then the accepted inputs in S and those outside S differ in number by at most

                    2 b |accepted| + (K - 1) ^ 2 · (|V| · |U| ^ (w + d + m) + |U| ^ #(unread inputs)).

                    Sweeps with extra messages #

                    noncomputable def Cslib.Circuits.Frontier.Network.frontierSweep {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) (π : Layout V) {S : Set (ι → U)} {R : Type u_5} (val : (ι → U) → E → U) (extra : ℕ → (ι → U) → R) (hlast : ∀ x ∈ S, ∀ y ∈ S, extra (Nat.card V + 1) x = extra (Nat.card V + 1) y) (hsplice : ∀ t ≤ Nat.card V, ∀ x ∈ S, ∀ y ∈ S, extra t x = extra t y → Set.EqOn (val x) (val y) (N.frontier π t) → ∀ (z : ι → U), Set.EqOn z x (N.readIn (π.initial t)) → Set.EqOn z y (N.readIn (π.initial t))ᶜ → z ∈ S) :
                    Sweep S (R × (E → Option U))

                    Sweeping along a layout with extra messages. Fix values val x on the edges for every input x, and an extra message extra t x at every time. The messages (extra t x, val x on the frontier at time t) form a sweep of S as soon as inputs of S with the same messages splice, and the last extra message is the same for all inputs of S.

                    Instances For
                      theorem Cslib.Circuits.Frontier.Network.ncard_transition_le {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) {π : Layout V} {S : Set (ι → U)} {R : Type u_5} {val : (ι → U) → E → U} {extra : ℕ → (ι → U) → R} {hlast : ∀ x ∈ S, ∀ y ∈ S, extra (Nat.card V + 1) x = extra (Nat.card V + 1) y} {hsplice : ∀ t ≤ Nat.card V, ∀ x ∈ S, ∀ y ∈ S, extra t x = extra t y → Set.EqOn (val x) (val y) (N.frontier π t) → ∀ (z : ι → U), Set.EqOn z x (N.readIn (π.initial t)) → Set.EqOn z y (N.readIn (π.initial t))ᶜ → z ∈ S} [Finite ι] [Finite U] [Finite E] {t : ℕ} (ht : t < Nat.card V) :
                      ((N.frontierSweep π val extra hlast hsplice).transition t '' S).ncard ≤ ((fun (x : ι → U) => (extra t x, extra (t + 1) x)) '' S).ncard * Nat.card U ^ ((N.frontier π t ∪ N.frontier π (t + 1)).ncard + (N.readAt ((Equiv.symm π) ⟨t, ht⟩)).ncard)

                      Transitions by width. At a step that processes a vertex, the transition is determined by the extra messages, the values on the two frontiers, and the inputs read at the vertex.

                      theorem Cslib.Circuits.Frontier.Network.ncard_transition_last_le {U : Type u_1} {ι : Type u_2} {V : Type u_3} {E : Type u_4} (N : Network U ι V E) {π : Layout V} {S : Set (ι → U)} {R : Type u_5} {val : (ι → U) → E → U} {extra : ℕ → (ι → U) → R} {hlast : ∀ x ∈ S, ∀ y ∈ S, extra (Nat.card V + 1) x = extra (Nat.card V + 1) y} {hsplice : ∀ t ≤ Nat.card V, ∀ x ∈ S, ∀ y ∈ S, extra t x = extra t y → Set.EqOn (val x) (val y) (N.frontier π t) → ∀ (z : ι → U), Set.EqOn z x (N.readIn (π.initial t)) → Set.EqOn z y (N.readIn (π.initial t))ᶜ → z ∈ S} [Finite ι] [Finite U] :
                      ((N.frontierSweep π val extra hlast hsplice).transition (Nat.card V) '' S).ncard ≤ ((fun (x : ι → U) => (extra (Nat.card V) x, extra (Nat.card V + 1) x)) '' S).ncard * Nat.card U ^ N.readᶜ.ncard

                      At the last step nothing is left on the frontier, and the transition is determined by the extra messages and the inputs that the network does not read.