Skip to content

SuperInstance/holonomy-harmony

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

13 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

holonomy-harmony

🎼 Chord progression analysis via holonomy β€” detect modulations, modal interchange, and cycle violations in harmony.

Holonomy-harmony proves that harmonic movement = cycle consistency. A chord progression has zero holonomy when it returns to its tonal center. When it doesn't, you've detected a modulation. This is constraint theory applied to music theory: the circle of fifths is a topological space, and chord progressions trace paths through it.

Why it exists

Music theory has always had an implicit spatial structure β€” the circle of fifths, the line of fifths, voice-leading spaces. But the connection between harmonic motion and topological holonomy (the "did you end up where you started?" invariant) hasn't been made explicit in a tool. This library makes that connection executable: every chord progression gets a holonomy number, and that number tells you exactly how "far from home" the harmony wandered.

The math in plain English

Holonomy measures whether a closed loop through a space returns you to the same orientation you started with. On the circle of fifths, each chord transition moves you clockwise (dominant direction) or counter-clockwise (subdominant direction). If you sum all those movements and get zero, the progression is tonally consistent β€” it returned home. If the sum is non-zero, you modulated.

Winding number counts how many full rotations around the circle of fifths the progression makes. A I-IV-V-I progression winds zero times. Coltrane's Giant Steps winds multiple times due to its major-third cycles.

Stability score (0–1) measures how "safe" a progression is: 1.0 = entirely diatonic, zero holonomy. 0.0 = highly chromatic with multiple modulations.

Quick start

pip install holonomy-harmony
from holonomy_harmony import analyze_progression, PROGRESSIONS

# Analyze the Pachelbel Canon progression in D major
symbols, tonic, mode = PROGRESSIONS["pachelbel_canon"]
result = analyze_progression(symbols, key_tonic=tonic, mode=mode)

print(f"Holonomy: {result.holonomy.holonomy}")        # -5
print(f"Winding:  {result.holonomy.winding_number}")   # 0.0833
print(f"Type:     {result.holonomy.progression_type}") # ProgressionType.MODULATION
print(f"Stability: {result.stability_score}")           # 0.35

# Analyze Giant Steps β€” much more adventurous
symbols, tonic, mode = PROGRESSIONS["giant_steps"]
result = analyze_progression(symbols, key_tonic=tonic, mode=mode)
print(f"Type:     {result.holonomy.progression_type}")  # ProgressionType.CHROMATIC
print(f"Stability: {result.stability_score}")            # lower

Output:

Holonomy: -5
Winding:  0.08333333333333333
Type:     ProgressionType.MODULATION
Stability: 0.35

Type:     ProgressionType.CHROMATIC_MEDIANT
Stability: 0.377

API overview

High-level: analyze_progression

from holonomy_harmony import analyze_progression

result = analyze_progression(
    symbols=["I", "vi", "IV", "V"],  # Roman numerals
    key_tonic=0,                      # C
    mode="major",                     # major or minor
    wrap=False,                       # treat as closed cycle?
)
# result.chords            -> List[Chord]
# result.holonomy          -> HolonomyResult
# result.graph             -> TonalGraph
# result.modulations       -> List[(index, description)]
# result.modal_interchanges -> List[(index, description)]
# result.stability_score   -> float (0.0-1.0)

Holonomy computation

from holonomy_harmony import compute_holonomy, winding_number, classify_progression

roots = [0, 7, 9, 5, 0]  # C, G, A, F, C

h = compute_holonomy(roots, wrap=True)
print(h.holonomy)       # net circle-of-fifths displacement
print(h.winding_number) # full rotations
print(h.max_deviation)  # furthest wander from tonic
print(h.is_consistent())# True if holonomy == 0

print(winding_number(roots))    # shortcut
print(classify_progression(roots))  # ProgressionType enum

Roman numeral parsing

from holonomy_harmony import parse_roman

chord = parse_roman("V7/vi", key_tonic=0, mode="major")
# Chord(root=10, quality='7', function='V7/vi',
#        is_secondary_dominant=True, implied_key=(9, 'major'))

Tonal graph

from holonomy_harmony import TonalGraph

g = TonalGraph()
g.build_from_progression([0, 7, 9, 5, 0])
print(g)  # <TonalGraph nodes=12 edges=4 total_weight=4.0>
print(g.adjacency_matrix())  # 12Γ—12 transition matrix
print(g.transition_probability(0, 7))  # P(G|C)

Built-in progressions

20 famous progressions included:

from holonomy_harmony import PROGRESSIONS

for name in PROGRESSIONS:
    symbols, tonic, mode = PROGRESSIONS[name]
    print(f"{name}: {' '.join(symbols)}")

Includes: pachelbel_canon, blues_12_bar, giant_steps, chopin_em_prelude, axis_progression, autumn_leaves, coltrane_changes, rhythm_changes, hey_jude, creep, take_five, and more.

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  analyzer.py│───>β”‚ cycle_checker │───>β”‚ tonal_graph  β”‚
β”‚             β”‚    β”‚               β”‚    β”‚              β”‚
β”‚ parse_roman β”‚    β”‚ compute_      β”‚    β”‚ TonalGraph   β”‚
β”‚ analyze_    β”‚    β”‚ holonomy      β”‚    β”‚ Transition   β”‚
β”‚ progression β”‚    β”‚ winding_      β”‚    β”‚ Direction    β”‚
β”‚ detect_     β”‚    β”‚ number        β”‚    β”‚ adjacency    β”‚
β”‚ modulations β”‚    β”‚ classify_     β”‚    β”‚ matrix       β”‚
β”‚ score_      β”‚    β”‚ progression   β”‚    β”‚              β”‚
β”‚ stability   β”‚    β”‚ HolonomyResultβ”‚    β”‚              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Input: Roman numerals β†’ Chord objects β†’ pitch-class roots
Process: roots β†’ circle-of-fifths steps β†’ holonomy signature
Output: HolonomyResult + stability score + modulation list

Documentation

Related repos

Requirements

  • Python 3.10+
  • No external dependencies (pure Python)

Install

pip install holonomy-harmony

Or from source:

git clone https://github.com/SuperInstance/holonomy-harmony.git
cd holonomy-harmony
pip install -e .

License

Apache License 2.0

About

🎼 Chord progression analysis via holonomy β€” detect modulations, modal interchange, and cycle violations in harmony

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors