Skip to main content

Holonomy-based chord progression analyzer โ€” proves harmonic movement = cycle consistency

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

holonomy_harmony-0.1.0.tar.gz (19.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

holonomy_harmony-0.1.0-py3-none-any.whl (14.2 kB view details)

Uploaded Python 3

File details

Details for the file holonomy_harmony-0.1.0.tar.gz.

File metadata

  • Download URL: holonomy_harmony-0.1.0.tar.gz
  • Upload date:
  • Size: 19.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for holonomy_harmony-0.1.0.tar.gz
Algorithm Hash digest
SHA256 84310414c9ec2a15f6a8cc4576a44a5841484eaeee6e1edb6d844eb2b2c18fc7
MD5 b8f46f4d1f7be64127e154ac8171b7a4
BLAKE2b-256 b6cfcbde5f92b217fb23f64d418edc777a406805e7eac03c8d51865374e35d66

See more details on using hashes here.

File details

Details for the file holonomy_harmony-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for holonomy_harmony-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 800acca77b4f3a480acb01318e08739b267a2117411afeea438d68163054af96
MD5 f802aadd027735ac0eabe5d34b018b2c
BLAKE2b-256 a719a2e75daa7f1b34aedeca945e3cb948d0221259b96c6845bb858f2b6d9cf2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page