Relative circuit complexity #
The complexity C(f | g) of f relative to g is the least number of gates needed to compute
f when the values of g come for free: the circuit reads an input x followed by the values
g x. Such a circuit only ever sees inputs of this form, which make up the graph of g, so
computing f given g is computing f of the first part of the input, on the graph of g.
Relative complexity is defined as this complexity on a support, C(f | g) = C^Γ(f ∘ π) where
Γ is the graph of g and π forgets the values of g. The reading in terms of circuits on
x and g x is recovered by ecomplexityGiven_le_iff.
The rules of relative complexity follow from the calculus of support complexity. Knowing g
never makes f harder, and knowing more never makes it harder either.
Computing g and then f from it gives the chain rule C(f) ≤ C(g) + C(f | g), and its
variant for computing f and g together. Relative complexity satisfies the triangle
inequality C(f | h) ≤ C(g | h) + C(f | g), and every function is free given itself.
The graph of g: every input followed by the values of g on it.
Equations
- Cslib.Circuits.graph g = Set.range fun (x : Fin n → U) => Fin.append x (g x)
Instances For
The complexity C(f | g) of f relative to g: the least size of a circuit that, reading
an input followed by the values of g on it, outputs the values of f on that input; ⊤ if
there is none. Such a circuit only ever sees inputs on the graph of g, so this is the complexity
of f of the first n inputs on that graph.
Equations
- Cslib.Circuits.ecomplexityGiven I f g = Cslib.Circuits.ecomplexityOn I (Cslib.Circuits.graph g) fun (z : Fin (n + k) → U) => f (z ∘ Fin.castAdd k)
Instances For
Relative complexity is complexity on the graph: computing f given g is computing f of
the first n inputs on the graph of g.
A circuit computes f of the first n inputs on the graph of g exactly when, reading an
input followed by the values of g on it, it outputs the values of f on that input.
Keeping the input while computing g costs no more than computing g.
Knowing g never makes f harder: C(f | g) ≤ C(f).
The chain rule: computing g and then f from it gives C(f) ≤ C(g) + C(f | g).
Computing f and g together costs at most computing f and then g given f.
The triangle inequality: C(f | h) ≤ C(g | h) + C(f | g). Given h, compute g, and then
f from g.
Every function is free given itself: C(f | f) = 0.
Over a complete basis #
The complexity C(f | g) of f relative to g over a complete basis, as a natural
number.
Equations
- Cslib.Circuits.complexityGiven I f g = Cslib.Circuits.complexityOn I (Cslib.Circuits.graph g) fun (z : Fin (n + k) → U) => f (z ∘ Fin.castAdd k)