The Obligatory Contour Principle #
The categorical, strict-identity, single-tier OCP: the ban on two identical adjacent
autosegments on one tier ([Leb73], [McC86]), on a projected tier for IsCleanOn
([CJ19]). As a stringset the constraint is tier-based strictly local
(TSL₂, [HRT11]), characterised in OCP. The
fusion repair collapse is mathlib's
List.destutter (· ≠ ·) ([Gol76], [McCP95]) — a retraction onto
cleanness, input-strictly-local ([CH18]); the blocking repair block is
antigemination, a guard rather than a retraction ([McC86]).
Gradient, similarity-scaled OCP ([FPB04]) is a different object and lives in the thresholded-TSL substrate, not here.
Main definitions #
OCP.IsClean/OCP.IsCleanOn— OCP-cleanness, flat and on a projected tier.OCP.collapse— the fusion repair (OCP-merger normal form).OCP.block— the blocking repair (antigemination guard).
Main results #
collapse_eq_destutter— the fusion repair isList.destutter.collapse_clean/collapse_eq_self_iff—collapseis the retraction ontoIsClean.collapse_sublist— fusion never adds material.collapse_append—collapseis the OCP congruence: collapsing each operand before++is harmless, socollapsedescends to the OCP quotient of(List α, ++)(the quotient monoid and its presentation⟨α | a · a = a⟩live on theCore.Algebra.FreeMonoid.Destuttersubstrate; the autosegmental reading is inAutosegmental.Collapse).block_eq_self— antigemination: a rule is blocked when it would violate the OCP.
The constraint #
A tier is OCP-clean when no adjacent pair is identical. Adjacency-only, so
weaker than List.Nodup ([1, 2, 1] is clean). A reducible alias of the substrate
predicate List.IsChain (· ≠ ·), so the substrate's Monoid {l // l.IsChain (· ≠ ·)}
applies to OCP-clean tiers downstream.
Equations
- OCP.IsClean xs = List.IsChain (fun (x1 x2 : α) => x1 ≠ x2) xs
Instances For
OCP on the tier projected from xs by keeping p-elements and reading proj
(tier-relativity, [CJ19]); flat IsClean is the p = ⊤, proj = id case.
Equations
- OCP.IsCleanOn p proj xs = OCP.IsClean (List.map proj (List.filter (fun (a : α) => decide (p a)) xs))
Instances For
Equations
- OCP.decidableIsClean xs = xs.instDecidableIsChainOfDecidableRel
Equations
- OCP.decidableIsCleanOn p proj x✝ = OCP.decidableIsCleanOn._aux_1 p proj x✝
The fusion repair #
OCP-merger normal form (the fusion repair): collapse each maximal run of identical adjacent elements into one. Fusing identical autosegments returns that same autosegment, so the payload is unchanged.
Equations
- OCP.collapse xs = List.destutter (fun (x1 x2 : α) => x1 ≠ x2) xs
Instances For
collapse fuses a constant run into its single autosegment.
collapse fixes a tier iff it is OCP-clean; with collapse_clean, the retraction
onto IsClean.
Normal-form lemmas #
Fusion never adds material: the repair output is a sublist of the input.
The OCP congruence: collapse as a quotient homomorphism #
collapse descends to a homomorphism on the OCP quotient of (List α, ++): collapsing
each operand before appending is harmless. This is what makes the OCP-clean tiers under
fusion-concatenation (List.destutterConcat) the quotient (List α, ++)/OCP — bundled on
the Core.Algebra.FreeMonoid.Destutter substrate as FreeMonoid.destutterHom.
The blocking repair #
The substrate provides the OCP quotient monoid (OCP-clean tiers under fusion-concatenation,
List.destutterConcat) and its identification with the monoid presented by idempotent
autosegments ⟨α | a · a = a⟩
(Core.Algebra.FreeMonoid.Destutter). The autosegmental reading of that quotient — the
decategorification square against the categorical representation, contrasting the OCP (a
monoidal quotient) with the No-Crossing Constraint (a monoidal subcategory) — lives
downstream in Autosegmental.Collapse, which can see both Autosegmental.ncc_isMonoidal and
Autosegmental.ocp_not_isMonoidal.
Blocking ([McC86]'s antigemination): apply rule only when it would not
create an OCP violation, else leave the input untouched — a guard preventing a process,
not a retraction repairing its output.
Equations
- OCP.block rule xs = if OCP.IsClean (rule xs) then rule xs else xs
Instances For
The run-index map #
Run index of upper position k in xs: the index (0-based) of the
OCP-run containing k, i.e. one less than the number of runs in the prefix
xs[0..k]. Concretely (OCP.collapse (xs.take (k+1))).length - 1.
Equations
- OCP.runIdx xs k = (OCP.collapse (List.take (k + 1) xs)).length - 1
Instances For
runIdx is monotone: later positions sit in later (or the same) runs.
On a constant tier every position lies in the single run: runIdx is 0.
Run-index and concatenation #
The defining property of concat for the OCP quotient: runIdx commutes with the
collapse-collapse seam (runIdx_append_collapse_left/right). On the A-block the prefix
run-index is untouched (runIdx_append_left, plus runIdx_clean re-reading a clean tier
as the identity); on the B-block the seam merges exactly when collapse xs ends in the
element heading ys (runIdx_append_right, the AR shadow of
List.destutter_append_length_clean).
The collapse of a prefix has the same head as the whole tier.
The B-part seam identity. Reading a suffix position through the collapse of an
append: the run-index is the left collapse's length plus the suffix's own run-index, minus
one exactly when the seam merges (List.destutter_append_length_clean).