| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Harmonic.Framework.Builder.PolyGen
Description
The foundation progression (T layer) is a plain gen
walk, byte-identical to it: same chain builder, same R constraints, same
entropy dial. Two partner triad chains (S/M layers) are then walked over
the finished foundation, one bar at a time. Each partner draws from a
fresh transition list fetched from ITS OWN previous state — every partner
bar is a corpus-valid continuation of its own layer history — filtered to
the polytonal overlap rules against that bar's foundation triad:
- each partner shares exactly 2 pitch classes with the foundation bar;
- the three triads union to exactly 5 pitch classes.
Those two rules admit exactly two geometries per bar, and the traversal chooses freely between them: COMMON-DYAD (all three triads share one dyad; every layer pair sounds 4 tones) and BASE-ANCHORED (the partners share different dyads of the foundation; T+S and T+M sound 4 tones, S+M sounds the full pentad). The hub-tone shape (three different dyads through one tone) unions to 4 and is excluded.
Partners honour the harmonic-space constraints only — key, allowed
roots, overtones — through the same R predicate as the walk
(matchesContextWithTarget with no bass target). Root-motion
direction specs, drift, pedal and inversion spacing bind the foundation
alone: the foundation owns the bass whenever it is present, and the
partner layers stay free to diverge.
Selection: jointly valid (S, M) pairs are ranked by summed own-list rank and drawn with ONE entropy-scaled gamma draw over the pair pool (the two-stage per-layer alternative saturates the entropy dial at the ~8- candidate second pool; the joint pool sits at ~100-324 where the dial is monotone — measured in archive/analysis/poly_seq.md). Supply relaxes down tiers when a list runs dry: one side from the space-constrained pure enumeration over all 220 absolute 3-PC sets, then both, then the unconstrained enumeration — the last is total, so partner selection can never fail (the study measured the list tier alone at 100% over 2,100 live steps; archive/analysis/poly_chain.md).
S/M identity is assigned once, after generation: the chain with the lower whole-layer dissonance total becomes S. Per-bar assignment would swap chain membership at ~45% of bar boundaries and destroy the very layer identity the chains provide.
Synopsis
- runPolyGen :: GenConfig -> IO (ProgressionContext, GenerationDiagnostics)
- runPolyGenFrom :: ProgressionContext -> Int -> Int -> GenConfig -> IO (ProgressionContext, GenerationDiagnostics)
Documentation
runPolyGen :: GenConfig -> IO (ProgressionContext, GenerationDiagnostics) Source #
Execute a PolyMode config: foundation walk (byte-identical to gen),
then the partner pass, then S/M assignment.
runPolyGenFrom :: ProgressionContext -> Int -> Int -> GenConfig -> IO (ProgressionContext, GenerationDiagnostics) Source #
Regenerate a contiguous range of bars within an existing polytonal
context. The foundation range regenerates exactly like a gen regen
(cue = the bar before the range, inferred by genFrom); both partner
chains regenerate over it, seeded from the KEPT partner bars before the
range, so every regenerated partner bar continues its own layer's
history. The source's S/M labelling is preserved — a partial regen
never reorders chains (that would relabel the kept bars).
Seam: when the regen does not cover the whole progression, the final regenerated bar's joint pool is additionally filtered to pairs whose partner states can continue onto the kept next partner bars as real graph edges — relaxed when empty (the study measured list-tier supply at 100%, so the filter is a preference, not a wall).