Circuit complexity #
The complexity of a function f on a support S, written C^S(f), is the least number of gates
in a circuit whose outputs agree with f on every input in S. The function may have several
values, one for each output of the circuit, and nothing is asked of the circuit outside S, so
C^S(f) depends only on the restriction of f to S. The complexity C(f) of f is its
complexity on all inputs, and the complexity of f relative to a function g, in
Cslib.Computability.Circuit.RelativeComplexity, is a complexity on the graph of g.
Over an arbitrary signature and interpretation some functions have no circuit at all, so
ecomplexityOn I S f takes values in ℕ∞, with ⊤ when no circuit computes f on S. Over a
complete basis, one over which every function has a circuit, the complexity is a natural number,
complexityOn I S f, and this is the notion of interest in the Boolean case.
Support complexity obeys a small calculus from which the rules for complexity and relative
complexity follow. It grows with the support and ignores the function outside the support.
Wiring, which only selects, permutes, or duplicates inputs, costs nothing. The composite g ∘ f
costs at most the complexity of f on S plus that of g on the image of S, and computing two
functions side by side costs at most the sum of their complexities.
See [Jukna, Chapter 1][Jukna2012] for the Boolean case.
References #
- [Stasys Jukna, Boolean Function Complexity: Advances and Frontiers][Jukna2012]
Following [Jukna, Section 1.1][Jukna2012], a basis is complete when every single-valued function, on every number of inputs, is computed by some circuit over it. Circuits here have no constant inputs, so this includes the constants, which is why NAND alone is not complete in this sense. Functions with several values then have circuits too, built by running circuits for their values side by side.
- exists_computes_single {n : ℕ} (f : (Fin n → U) → U) : ∃ (c : Circuit σ n 1), c.Computes I fun (x : Fin n → U) (x_1 : Fin 1) => f x
Every single-valued function has a circuit.
Instances
Over a complete basis every function, with any number of values, has a circuit.
The complexity C^S(f) of f on the support S: the least size of a circuit computing f
on S under I, or ⊤ if there is none.
Equations
- Cslib.Circuits.ecomplexityOn I S f = ⨅ (c : { c : Cslib.Circuits.Circuit σ n m // c.ComputesOn I S f }), ↑(↑c).size
Instances For
The complexity C(f) of f: its complexity on all inputs.
Equations
Instances For
Complexity on a support #
When some circuit computes f on S, the least size is attained.
A larger support is harder to compute on.
Complexity on a support depends only on the values of the function on the support.
Selecting, permuting, or duplicating inputs costs nothing.
The composition rule: computing g ∘ f on S costs at most computing f on S and then
g on the values f takes there.
The pairing rule: computing f and g side by side on S costs at most the sum of their
complexities on S.
Reading the inputs through a wiring costs nothing beyond computing f on the rewired
support.
Complexity on all inputs #
When some circuit computes f, the least size is attained.
Computing f alongside g is at least as hard as computing f.
Computing g alongside f is at least as hard as computing g.
Over a complete basis #
The complexity C^S(f) of f on the support S over a complete basis, as a natural
number.
Equations
- Cslib.Circuits.complexityOn I S f = WithTop.untop (Cslib.Circuits.ecomplexityOn I S f) ⋯
Instances For
The complexity C(f) of f over a complete basis, as a natural number.
Equations
Instances For
A lower bound on the complexity is a lower bound on the size of every circuit.
Over a complete basis the least size is attained.
Over a complete basis, lower bounds on complexity are exactly lower bounds on the size of every circuit.
A synthesis bound on the input projections bounds the complexity.