Expand description
The CommonSubexpressionOptimizer: a meta-transformer which factors common subexpressions out
of a Circuit into intermediate witnesses, reducing the total number of opcodes.
The name comes from its net effect. The sub-passes below collectively detect subexpressions that
the circuit computes more than once and bind each of them to a single intermediate witness, so
the value is constrained once and then reused. csat::CSatTransformer is the pass that forms
the candidate subexpressions (by slicing wide expressions into backend-width-sized chunks, caching
identical chunks so the same subexpression maps to the same witness), and
merge_expressions::MergeExpressionsOptimizer is the pass that discards the candidates which
turned out not to be common (those used in only two opcodes are merged back). What survives are the
genuinely shared subexpressions.
§On opcode “width”
Several passes here are parameterized by a width. This is not a hard upper bound on the size
of an emitted opcode: ACIR places no limit on how many terms an AssertZero opcode may contain,
and proving backends are expected to handle linear combinations of unbounded width. The width is
a heuristic target that controls the granularity at which the csat::CSatTransformer slices
expressions, which in turn determines which subexpressions become reuse candidates. Because it is
only a target, an emitted opcode may legitimately exceed it — see csat::CSatTransformer for the
cases where this happens (e.g. an opcode whose sole unknown sits inside a multiplication term, which
cannot be sliced without making the circuit unsolvable). Solvability, checked by
crate::compiler::CircuitSimulator, is the property these passes actually preserve; width is not.
§Sub-passes
§CSAT: slices AssertZero opcodes towards the backend’s preferred width.
For instance, with a width of 4, the AssertZero opcode x1 + x2 + x3 + x4 + x5 - y = 0 is sliced using
2 intermediate variables (z1, z2):
x1 + x2 + x3 = z1
x4 + x5 = z2
z1 + z2 - y = 0If x1,..x5 are inputs to the program, they are tagged as ‘solvable’, and would be used to compute the value of y.
If we generated the intermediate variable x4 + x5 - y = z3 instead, we would get an unsolvable circuit because
that AssertZero opcode has two unknown values: y and z3.
So the CSAT transformation keeps track of which witnesses would be solved for each opcode in order to only generate
solvable intermediate variables. Identical slices are cached, so a subexpression appearing in several opcodes is
assigned a single shared intermediate witness — this is where the common subexpressions are formed.
§Eliminate intermediate variables
The ‘eliminate intermediate variables’ pass will remove any intermediate variables (for instance created by the previous transformation)
that are used in exactly two AssertZero opcodes.
This results in arithmetic opcodes having linear combinations of potentially large width.
For instance if the intermediate variable is z1 and is only used in y:
z1 = x1 + x2 + x3
y = z1 + x4We remove it, undoing the work done during the CSAT transformation: y = x1 + x2 + x3 + x4.
We do this because the backend is expected to handle linear combinations of ‘unbounded width’ in a more efficient way than the ‘CSAT transformation’. However, it is worthwhile to keep an intermediate variable if it is used in more than two opcodes: that is precisely a common subexpression, and materializing it once is cheaper than recomputing it in each opcode.
§redundant_range
The ‘range optimization’ pass, from the optimizers module, will remove any redundant range opcodes.
Modules§
Structs§
- Witness
Folder 🔒 - Fold all witnesses in a circuit.
Constants§
- DEFAULT_
EXPRESSION_ 🔒WIDTH - DEFAULT_
MAX_ 🔒TRANSFORMER_ PASSES - We use multiple passes to stabilize the output in many cases
Functions§
- max_
witness 🔒 - Find the witness with the highest ID in the circuit.
- transform_
internal 🔒 - Applies backend specific optimizations to a
Circuit. - transform_
internal_ 🔒once - Accepts an injected
acir_opcode_positionsto allow transformations to be applied directly after optimizations.