Arithmetic with heads: addition of ranks #
A head holds an element of the universe, that is – through
DescriptiveComplexity.orank – a number below Nat.card A. This file teaches a
DescriptiveComplexity.HeadProgram to add two of them, which is the first of
the fragments the inclusion AC⁰ ⊆ LOGSPACE needs: the numeric predicates of
FirstOrder.Language.arith have to be decided by a machine whose
guards are quantifier-free, and “these two heads are equal” is all such a guard
can say.
The walk #
DescriptiveComplexity.HeadProgram.plusP i j k a b mk decides
orank (x i) + orank (x j) = orank (x k) with two scratch heads a, b and a
marker head mk. It is a five-node control graph
(DescriptiveComplexity.HeadProgram.PlusNode), every node a leafP or a
moveP:
| node | fragment | true → | false → |
|---|---|---|---|
init | a := copy i, b := toMin, mk := toMax | test | test |
test | b = j? | check | over |
check | a = k? | exit true | exit false |
over | a = mk? | exit false | step |
step | a := succ a, b := succ b | test | test |
so a runs up from x i while b counts up from the least element, and the
answer is read off when b arrives at x j. The invariant is
DescriptiveComplexity.HeadProgram.PlusBase: orank (x i) + orank (z b) = orank (z a), the counter is still below orank (x j), and the marker has not
been passed.
Why the marker, and why the fragment parks it itself #
“This head is at the greatest element” is not a quantifier-free fact of one head,
while “these two heads are equal” is, so the overflow test compares a with a
head parked at the maximum – the dmk idiom of
DescriptiveComplexity.HeadCaptureDet. Overflow must exit false rather
than die: a disabled succ transition is sound but not complete.
The marker is not an input the caller has to have parked, as in
DescriptiveComplexity.HeadProgram.lexNextP: it is a third scratch head, sent
to the greatest element by the fragment's own first move (HeadMove.toMax).
That single choice is what makes the specification clean. A caller-supplied
marker can sit anywhere, and the walk then has three regimes – exit false
early, answer correctly, or run off the end with no exit at all – so its
specification would have to be stated by the marker's value, as lexRel is.
Parking it internally collapses all of that: the marker is the maximum, so a sum
that does not fit is detected exactly when it does not fit, and a sum that does
not fit is not the rank of anything either. What the fragment runs is therefore
just DescriptiveComplexity.HeadProgram.Decides, with no side condition
(DescriptiveComplexity.HeadProgram.decides_plusP).
Where the levels go #
The control walk is assembled at protection level K – every head protected –
because a fragment relation that mentions the scratch heads is not local at
the caller's level, and DescriptiveComplexity.HeadProgram.runs_wireP needs
locality. At level K locality is free
(DescriptiveComplexity.HeadProgram.headLocal2_top), and the result is weakened
to the caller's level afterwards, which is where the three scratch heads are
forgotten.
Locality is free at the top #
Every relation is local at level K: there is no head beyond the last
one to differ in. This is what lets a control walk over fragments that use
scratch heads be assembled without any locality proof, the protection level
being lowered only afterwards.
Dependency graph
The control graph of an addition #
The control nodes of an addition: initialize the two scratch heads, test whether the counter has arrived, read the answer off, test for overflow, step.
- init : PlusNode
Copy the first summand onto
aand send the counterbto the least element. - test : PlusNode
Has the counter reached the second summand?
- check : PlusNode
It has: is
athe claimed sum? - over : PlusNode
It has not: is
aat the marker, i.e., would the next step overflow? - step : PlusNode
Step both scratch heads.
Instances For
Dependency graph
Dependency graph
Dependency graph
The wiring of an addition: the loop test → over → step → test, with the
two answers hanging off check and the overflow answer off over.
Equations
- One or more equations did not get rendered due to their size.
- DescriptiveComplexity.HeadProgram.plusWire DescriptiveComplexity.HeadProgram.PlusNode.init x✝ = Sum.inl DescriptiveComplexity.HeadProgram.PlusNode.test
- DescriptiveComplexity.HeadProgram.plusWire DescriptiveComplexity.HeadProgram.PlusNode.check x✝ = Sum.inr x✝
- DescriptiveComplexity.HeadProgram.plusWire DescriptiveComplexity.HeadProgram.PlusNode.over x✝ = if x✝ = true then Sum.inr false else Sum.inl DescriptiveComplexity.HeadProgram.PlusNode.step
- DescriptiveComplexity.HeadProgram.plusWire DescriptiveComplexity.HeadProgram.PlusNode.step x✝ = Sum.inl DescriptiveComplexity.HeadProgram.PlusNode.test
Instances For
Dependency graph
The moves of the initialization: the running head copies the first summand, the counter goes to the least element, and the marker is parked at the greatest element – which is what makes the specification of the fragment clean.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The moves of one step: both scratch heads advance.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The fragments of an addition.
Equations
- One or more equations did not get rendered due to their size.
- DescriptiveComplexity.HeadProgram.plusFam i j k a b mk DescriptiveComplexity.HeadProgram.PlusNode.test = DescriptiveComplexity.HeadProgram.leafP (DescriptiveComplexity.HeadMove.eqVarF L b j) ⋯
- DescriptiveComplexity.HeadProgram.plusFam i j k a b mk DescriptiveComplexity.HeadProgram.PlusNode.check = DescriptiveComplexity.HeadProgram.leafP (DescriptiveComplexity.HeadMove.eqVarF L a k) ⋯
- DescriptiveComplexity.HeadProgram.plusFam i j k a b mk DescriptiveComplexity.HeadProgram.PlusNode.over = DescriptiveComplexity.HeadProgram.leafP (DescriptiveComplexity.HeadMove.eqVarF L a mk) ⋯
Instances For
Dependency graph
Addition: decide orank (x i) + orank (x j) = orank (x k), using the
scratch heads a, b and a marker mk.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
What the fragments run #
The relations the fragments of an addition run, at the top protection level.
Equations
- One or more equations did not get rendered due to their size.
- DescriptiveComplexity.HeadProgram.plusFamRel i j k a b mk DescriptiveComplexity.HeadProgram.PlusNode.test = fun (x : Fin K → A) (c : Bool) (y : Fin K → A) => (c = true ↔ x b = x j) ∧ y = x
- DescriptiveComplexity.HeadProgram.plusFamRel i j k a b mk DescriptiveComplexity.HeadProgram.PlusNode.check = fun (x : Fin K → A) (c : Bool) (y : Fin K → A) => (c = true ↔ x a = x k) ∧ y = x
- DescriptiveComplexity.HeadProgram.plusFamRel i j k a b mk DescriptiveComplexity.HeadProgram.PlusNode.over = fun (x : Fin K → A) (c : Bool) (y : Fin K → A) => (c = true ↔ x a = x mk) ∧ y = x
Instances For
Dependency graph
An equality test between two heads, as a fragment at the top protection level: it answers the equality and moves nothing.
Dependency graph
Dependency graph
The relation an addition runs #
The invariant of the loop, at the nodes inside it: the running head is the first summand plus the counter, the counter has not passed the second summand, and the marker is parked at the greatest element. Nothing has to be said about the marker having been passed: a rank below the running head's is below the maximum.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The invariant of the control walk of an addition, node by node.
Equations
- One or more equations did not get rendered due to their size.
- DescriptiveComplexity.HeadProgram.PlusInv i j a b mk x DescriptiveComplexity.HeadProgram.PlusNode.init = fun (z : Fin K → A) => z = x
- DescriptiveComplexity.HeadProgram.PlusInv i j a b mk x DescriptiveComplexity.HeadProgram.PlusNode.test = fun (z : Fin K → A) => DescriptiveComplexity.HeadProgram.PlusBase i j a b mk x z
- DescriptiveComplexity.HeadProgram.PlusInv i j a b mk x DescriptiveComplexity.HeadProgram.PlusNode.over = fun (z : Fin K → A) => DescriptiveComplexity.HeadProgram.PlusBase i j a b mk x z ∧ z b ≠ x j
- DescriptiveComplexity.HeadProgram.PlusInv i j a b mk x DescriptiveComplexity.HeadProgram.PlusNode.step = fun (z : Fin K → A) => DescriptiveComplexity.HeadProgram.PlusBase i j a b mk x z ∧ z b ≠ x j
Instances For
Dependency graph
Soundness: the invariant of the control walk #
The head indices a caller must respect: the four interface heads are protected, the two scratch heads are not.
The first summand is protected.
The second summand is protected.
The claimed sum is protected.
The running head is scratch.
The counter is scratch.
The marker is scratch too: the fragment parks it itself.
The three scratch heads are distinct: being scratch does not make them so.
The running head is not the marker.
The counter is not the marker.
Instances For
Dependency graph
i and mk are distinct heads, being on opposite sides of the
protection level.
Dependency graph
j and a are distinct heads, being on opposite sides of the
protection level.
Dependency graph
j and b are distinct heads, being on opposite sides of the
protection level.
Dependency graph
j and mk are distinct heads, being on opposite sides of the
protection level.
Dependency graph
k and a are distinct heads, being on opposite sides of the
protection level.
Dependency graph
k and b are distinct heads, being on opposite sides of the
protection level.
Dependency graph
k and mk are distinct heads, being on opposite sides of the
protection level.
Dependency graph
The two arithmetic scratch heads are distinct, by fiat.
Dependency graph
The running head is not the marker, by fiat.
Dependency graph
The counter is not the marker, by fiat.
Dependency graph
The moves of the initialization, read off: the running head, the counter, the marker, and everything else.
Dependency graph
The moves of a step, read off.
Dependency graph
The invariant holds all along the control walk: this is the whole soundness argument, the exits being read off it.
Dependency graph
Completeness: building the walk #
The state after t iterations is reachable, as long as the counter has
not passed the second summand and the running head still fits in the universe.
No condition on the marker: the walk parks it itself, at the top, so it is
reached exactly when the sum does not fit.
Dependency graph
What an addition decides #
The addition fragment decides the addition, with no side condition: it parks its own marker, so overflow is detected exactly where the sum leaves the universe, and a sum that leaves the universe is not the rank of the third head either.
Dependency graph
The addition fragment is deterministic: every node of the control graph
is, and DescriptiveComplexity.HeadProgram.wireP inherits it. This is what makes
the assembled machine a deterministic automaton, hence FO(DTC) rather than
merely FO(TC).
Dependency graph
The control graph of a multiplication #
The control nodes of a multiplication: an outer loop counting the rounds and, inside it, a scan looking for the element that carries the next partial product.
- init : TimesNode
Send the accumulator and the round counter to the least element, and the scan's marker to the greatest.
- outer : TimesNode
Has the round counter reached the second factor?
- final : TimesNode
It has: is the accumulator the claimed product?
- scanInit : TimesNode
It has not: start the scan at the least element.
- probe : TimesNode
Does the candidate carry the accumulator plus the first factor?
- scanOver : TimesNode
It does not: is the candidate at the marker, i.e., would the product overflow?
- scanStep : TimesNode
Step the candidate.
- commit : TimesNode
The candidate carries the next partial product: take it, and count the round.
Instances For
Dependency graph
Dependency graph
Dependency graph
The wiring of a multiplication: the outer loop outer → scan → commit → outer, the scan probe → scanOver → scanStep → probe, and the two answers
hanging off final and off the scan's overflow test.
Equations
- One or more equations did not get rendered due to their size.
- DescriptiveComplexity.HeadProgram.timesWire DescriptiveComplexity.HeadProgram.TimesNode.init x✝ = Sum.inl DescriptiveComplexity.HeadProgram.TimesNode.outer
- DescriptiveComplexity.HeadProgram.timesWire DescriptiveComplexity.HeadProgram.TimesNode.final x✝ = Sum.inr x✝
- DescriptiveComplexity.HeadProgram.timesWire DescriptiveComplexity.HeadProgram.TimesNode.scanInit x✝ = Sum.inl DescriptiveComplexity.HeadProgram.TimesNode.probe
- DescriptiveComplexity.HeadProgram.timesWire DescriptiveComplexity.HeadProgram.TimesNode.scanStep x✝ = Sum.inl DescriptiveComplexity.HeadProgram.TimesNode.probe
- DescriptiveComplexity.HeadProgram.timesWire DescriptiveComplexity.HeadProgram.TimesNode.commit x✝ = Sum.inl DescriptiveComplexity.HeadProgram.TimesNode.outer
Instances For
Dependency graph
The moves that start a multiplication: accumulator and round counter to the least element, the scan's marker to the greatest.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The move that starts a scan: the candidate to the least element.
Equations
Instances For
Dependency graph
The move of one scan step: the candidate advances.
Equations
Instances For
Dependency graph
The moves that close a round: the accumulator takes the candidate's value and the round counter advances.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The fragments of a multiplication. The probe node is a whole addition
program (DescriptiveComplexity.HeadProgram.plusP), which is what makes the
scan work: it asks whether the candidate carries the accumulator plus the first
factor.
Equations
- One or more equations did not get rendered due to their size.
- DescriptiveComplexity.HeadProgram.timesFam i j k acc cnt cand tmk a b mk DescriptiveComplexity.HeadProgram.TimesNode.probe = DescriptiveComplexity.HeadProgram.plusP acc i cand a b mk
Instances For
Dependency graph
Multiplication: decide orank (x i) * orank (x j) = orank (x k), by
adding the first factor to an accumulator once per round.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The head layout of a multiplication #
The head indices a caller of a multiplication must respect. Three levels: the
interface below p, the four working heads between p and m, and the
addition's own three scratch heads at m and above – the last group being
exactly what DescriptiveComplexity.HeadProgram.PlusHeads asks of the addition
called at the probe node.
The first factor is interface.
The second factor is interface.
The claimed product is interface.
The accumulator is a working head.
The round counter is a working head.
The scan's candidate is a working head.
The scan's marker is a working head.
- hplus : PlusHeads acc i cand a b mk m
The accumulator is not the round counter.
The accumulator is not the candidate.
The accumulator is not the scan's marker.
The round counter is not the candidate.
The round counter is not the scan's marker.
The candidate is not the scan's marker.
Instances For
Dependency graph
What the fragments of a multiplication run #
The relations the fragments of a multiplication run, at the level m that
protects the working heads and leaves the addition's scratch heads free.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
Dependency graph
Dependency graph
A multiplication is deterministic: every node of its control graph is –
the probe node because an addition is
(DescriptiveComplexity.HeadProgram.deterministic_plusP).
Dependency graph
The invariant of a multiplication #
The invariant of both loops: the accumulator carries the first factor times
the round counter, the round counter has not passed the second factor, the scan's
marker is at the greatest element, and nothing else below m has moved.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The invariant of the control walk of a multiplication, node by node. Inside the scan it also records how far the candidate has got: below the target while the probe keeps failing, at it when the probe succeeds.
Equations
- One or more equations did not get rendered due to their size.
- DescriptiveComplexity.HeadProgram.TimesInv i j acc cnt cand tmk m x DescriptiveComplexity.HeadProgram.TimesNode.init = fun (z : Fin K → A) => z = x
Instances For
Dependency graph
The interface heads a multiplication must not disturb are below every working head.
Dependency graph
The invariant reads only the heads below m, so it survives a fragment that
dirties the addition's scratch heads.
Dependency graph
The moves that start a multiplication, read off.
Dependency graph
The move that starts a scan, read off.
Dependency graph
The move of one scan step, read off.
Dependency graph
The moves that close a round, read off.
Dependency graph
The invariant holds all along the control walk of a multiplication.
Dependency graph
Completeness: building the two walks #
The scan reaches every candidate up to its target: from the start of a scan, the walk can be continued until the candidate has any prescribed rank at most the target's, the probe failing at every earlier one.
Dependency graph
The outer loop reaches every round whose partial product fits: the state
after r rounds is reachable, the accumulator carrying orank (x i) * r.
Dependency graph
What a multiplication decides #
The multiplication fragment decides the multiplication, with no side condition: the scan's marker is parked by the fragment itself, as the addition's is, so a product that leaves the universe is detected in the round where it does and is not the rank of the third head either.