Metadata-Version: 2.4
Name: euclidean-rhythm
Version: 0.3.0
Summary: Euclidean rhythms (Bjorklund) with Toussaint evenness, Keith syncopation, and Pressing rhythmic oddity, in pure Python.
Project-URL: Homepage, https://github.com/amaar-mc/euclidean-rhythm
Project-URL: Repository, https://github.com/amaar-mc/euclidean-rhythm
Project-URL: Issues, https://github.com/amaar-mc/euclidean-rhythm/issues
Project-URL: Changelog, https://github.com/amaar-mc/euclidean-rhythm/blob/main/CHANGELOG.md
Author: Amaar Chughtai
License: MIT License
        
        Copyright (c) 2026 Amaar Chughtai
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: bjorklund,euclidean-rhythm,generative-music,music,music-theory,python,rhythm,toussaint
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Scientific/Engineering
Classifier: Typing :: Typed
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: hypothesis>=6; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# euclidean-rhythm

<p align="center">
  <img src="assets/logo.png" alt="euclidean-rhythm logo" width="160">
</p>

Generate Euclidean rhythms and analyze them with standard geometric measures, in pure Python with zero dependencies.

## What are Euclidean rhythms?

Euclidean rhythms distribute `k` onsets as evenly as possible over `n` time steps using
Bjorklund's algorithm - the same Euclidean GCD logic that underlies many traditional
musical patterns worldwide.

**The son clave** (3 onsets, 8 steps): `x . . x . . x .`
**The bossa nova clave** (5 onsets, 8 steps): `x . x x . x x .`

## Install

```sh
pip install euclidean-rhythm
```

## Quick start

```python
from euclidean_rhythm import (
    complement,
    euclidean,
    evenness,
    inter_onset_intervals,
    ioi_histogram,
    is_euclidean,
    necklace,
    offbeatness,
    onset_positions,
    pattern_from_onsets,
    rhythmic_oddity,
    rotate,
    syncopation,
)

# Generate rhythms
son = euclidean(pulses=3, steps=8)
# [1, 0, 0, 1, 0, 0, 1, 0]

bossa = euclidean(pulses=5, steps=8)
# [1, 0, 1, 1, 0, 1, 1, 0]

# Rotate
rotate(son, steps=2)
# [0, 1, 0, 0, 1, 0, 1, 0]

# Canonical necklace form (rotation-invariant)
necklace(son) == necklace(rotate(son, steps=3))
# True

# Recognize Euclidean rhythms (any rotation of E(k, n))
is_euclidean(son)
# True
is_euclidean(rotate(son, steps=2))
# True
is_euclidean([1, 1, 1, 0, 0, 0, 0, 0])  # clustered, not even
# False

# Rhythmic complement (swap onsets and rests)
complement(son)
# [0, 1, 1, 0, 1, 1, 0, 1]

# Evenness (1.0 = maximally even)
evenness(euclidean(pulses=4, steps=8))
# 1.0

# Keith syncopation (0 = no syncopation)
syncopation(euclidean(pulses=4, steps=8))
# 0

# Pressing rhythmic oddity
rhythmic_oddity(son)
# True

# Off-beat onset count (Toussaint offbeatness)
offbeatness(son)
# 1  -- onset at position 3 is off-beat in n=8; 0 and 6 are on-beat

# Inter-onset intervals (gaps in pulses, wrapping)
inter_onset_intervals(son)
# [3, 3, 2]  -- sums to 8

# Histogram of inter-onset intervals
ioi_histogram(son)
# {3: 2, 2: 1}

# Convert between 0/1 pattern and onset-position list
onset_positions(son)
# [0, 3, 6]
pattern_from_onsets(positions=[0, 3, 6], steps=8)
# [1, 0, 0, 1, 0, 0, 1, 0]
```

## CLI

```sh
euclidean-rhythm 3 8
# x..x..x.

euclidean-rhythm 5 8
# x.xx.xx.
```

## API

All parameters are keyword-only.

| Function | Description |
|---|---|
| `euclidean(*, pulses, steps)` | Generate Euclidean rhythm (Bjorklund's algorithm) |
| `rotate(rhythm, *, steps)` | Rotate left by steps (mod len) |
| `necklace(rhythm)` | Lexicographically minimal rotation (canonical form) |
| `complement(rhythm)` | Swap onsets and rests (1 <-> 0), preserving length |
| `is_euclidean(rhythm)` | True iff the rhythm is a rotation of E(k, n) |
| `evenness(rhythm)` | Toussaint geometric evenness in (0, 1] |
| `syncopation(rhythm)` | Keith (1991) syncopation count |
| `rhythmic_oddity(rhythm)` | Pressing (1983) rhythmic oddity property |
| `offbeatness(rhythm)` | Count of onsets on off-beat positions (gcd-coprime to n) |
| `inter_onset_intervals(rhythm)` | Gaps in pulses between consecutive onsets, wrapping |
| `ioi_histogram(rhythm)` | Histogram of inter-onset interval lengths |
| `onset_positions(rhythm)` | Indices of onsets in a 0/1 pattern |
| `pattern_from_onsets(*, positions, steps)` | Build 0/1 pattern from onset indices |

## Measures defined

**Evenness** (Toussaint 2005): Place onsets on a unit circle; sum all pairwise chord
lengths; normalize by the maximum (equally spaced onsets). Score 1.0 means maximally even.

**Syncopation** (Keith 1991): Metric weight of position `i` is `n` for the downbeat
(`i=0`) and the largest power of 2 dividing `i` for `i>0`. A syncopation occurs when
an onset at a weak beat is followed by a rest at a stronger beat; the score accumulates
the weight difference.

**Rhythmic oddity** (Pressing 1983): True if no two onsets are diametrically opposite
on the rhythm circle (no pair partitions the cycle into two equal halves).

**Offbeatness** (Toussaint): For a cycle of n pulses, position p is off-beat iff
gcd(p, n) == 1 -- equivalently, p is not covered by any regular subdivision of the
cycle (union of {k*n/d} for proper divisors d of n). Offbeatness is the count of onsets
at such positions. Both characterizations produce identical off-beat sets, verified for
n in 2..64.

**Inter-onset intervals**: The sequence of gaps (in pulses) between consecutive onsets
around the cycle, wrapping from the last onset back to the first. Always sums to n.

**Is-Euclidean**: A rhythm is Euclidean if it equals some rotation of the canonical
Euclidean rhythm E(k, n) for its own onset count k and length n. Equivalently,
`necklace(rhythm) == necklace(euclidean(pulses=k, steps=n))`. This is a test for maximal
evenness up to rotation; the all-rest (k=0) and all-onset (k=n) rhythms are trivially
Euclidean.

**Complement**: Swap every onset and rest (1 <-> 0), preserving length. The complement of
a rhythm with k onsets over n steps has n - k onsets, and applying it twice returns the
original (an involution). The complement of E(k, n) is generally not E(n - k, n), since
swapping onsets and rests does not in general preserve maximal evenness.

## References

- Bjorklund, E. (2003). The theory of rep-rate pattern generation in the SNS timing system.
- Toussaint, G. (2005). The Euclidean algorithm generates traditional musical rhythms. BRIDGES.
- Keith, M. (1991). From Polychords to Polya: Adventures in Musical Combinatorics.
- Pressing, J. (1983). Cognitive isomorphisms between pitch and rhythm in world musics. Studies in Music.

## License

MIT. Copyright (c) 2026 Amaar Chughtai.
