Math Series#
Match a biosignal’s peak ratios against classic mathematical sequences (Fibonacci, Lucas, harmonics, Farey, …) to find which is most present, and derive scales from the best match. Walkthrough: Matching biosignals to mathematical series.
biotuner.math_series — match biosignal ratios to mathematical sequences.
Module type: Object
Generate ratio sets from classic integer sequences (Fibonacci, Lucas, Padovan, Pell, Jacobsthal, Mersenne, Hofstadter-Q, harmonics, subharmonics, triangular, Farey), score how present each series is in a biosignal’s peak ratios, and derive musical structures (scales and consonance-selected modes) from the matched subset of a series.
The entry point is the math_series pipeline, which accepts either
a fitted compute_biotuner instance or a
HarmonicInput descriptor, normalises it to a
single list of octave-folded peak ratios, and compares those against each
series. Matching conservatism is controlled by maxdenom (the maximum
denominator of the rational approximation used to decide whether two ratios
are “the same”): a low maxdenom collapses nearby ratios onto simple
fractions (lenient matching), a high maxdenom keeps fine distinctions
(strict matching).
- fibonacci(order: int) List[int][source]#
Generate the first
orderFibonacci numbers.Examples
>>> fibonacci(8) [0, 1, 1, 2, 3, 5, 8, 13]
- lucas(order: int, seed: Sequence[int] = (2, 1)) List[int][source]#
Generate the first
orderterms of a Lucas-type sequence.- Parameters:
order (int) – Number of terms to return.
seed (sequence of int, default=(2, 1)) – The two starting values.
(2, 1)is the classic Lucas sequence.
Examples
>>> lucas(7) [2, 1, 3, 4, 7, 11, 18]
- padovan(order: int) List[int][source]#
Generate the first
orderPadovan numbers.Examples
>>> padovan(7) [1, 1, 1, 2, 2, 3, 4]
- pell(order: int) List[int][source]#
Generate the first
orderPell numbers.Examples
>>> pell(6) [0, 1, 2, 5, 12, 29]
- jacobsthal(order: int) List[int][source]#
Generate the first
orderJacobsthal numbers.Examples
>>> jacobsthal(6) [0, 1, 1, 3, 5, 11]
- mersenne(order: int) List[int][source]#
Generate the first
orderMersenne numbers2**i - 1.Examples
>>> mersenne(5) [0, 1, 3, 7, 15]
- hofstadter_q(order: int) List[int][source]#
Generate the first
orderterms of the Hofstadter Q sequence.Examples
>>> hofstadter_q(8) [1, 1, 2, 3, 3, 4, 5, 5]
- harmonics(order: int) List[int][source]#
Generate the first
orderterms of the harmonic series.Examples
>>> harmonics(5) [1, 2, 3, 4, 5]
- subharmonics(order: int) List[float][source]#
Generate the first
orderterms of the subharmonic series.Examples
>>> subharmonics(4) [1.0, 0.5, 0.3333333333333333, 0.25]
- triangular(order: int) List[int][source]#
Generate the first
ordertriangular numbers.Examples
>>> triangular(5) [1, 3, 6, 10, 15]
- farey(order: int) List[Tuple[int, int]][source]#
Generate the Farey sequence of a given
orderas(num, den)pairs.Unlike the integer sequences, each Farey term is already a fraction in
[0, 1]; the returned(num, den)pairs are treated directly as ratios (num / den) byseries_ratio_pairs().Examples
>>> farey(4) [(0, 1), (1, 4), (1, 3), (1, 2), (2, 3), (3, 4), (1, 1)]
- SERIES_FUNCS = {'farey': <function farey>, 'fibonacci': <function fibonacci>, 'harmonics': <function harmonics>, 'hofstadter_q': <function hofstadter_q>, 'jacobsthal': <function jacobsthal>, 'lucas': <function lucas>, 'mersenne': <function mersenne>, 'padovan': <function padovan>, 'pell': <function pell>, 'subharmonics': <function subharmonics>, 'triangular': <function triangular>}#
Mapping of series name -> generator. Add a row here to register a series.
- series_ratio_pairs(name: str, order: int, octave: float = 2.0, which: str = 'both', lucas_seed: Sequence[int] = (2, 1)) List[Tuple[float, Tuple[float, float]]][source]#
Build the octave-folded ratio set of a series, keeping pair provenance.
- Parameters:
name (str) – A key of
SERIES_FUNCS.order (int) – Number of terms (or, for
"farey", the Farey order).octave (float, default=2.0) – Period the ratios are folded into (
[1, octave)).which ({‘both’, ‘high/low’, ‘low/high’}, default=’both’) – For integer sequences, whether to keep ratios where the first element is larger (
high/low), smaller (low/high), or both. Ignored for"farey"(whose terms are already fractions).lucas_seed (sequence of int, default=(2, 1)) – Starting pair forwarded to
lucas()whenname == "lucas".
- Returns:
list of (float, (float, float)) – Each entry is
(folded_ratio, (element_1, element_2)). The pair is kept unfolded so it can be plotted on the element/element scatter.
- class math_series(source: Any | None = None, ratios: Sequence[float] | None = None, *, ratios_source: str = 'peaks_ratios', series_names: Sequence[str] | None = None, order: int = 20, maxdenom: int = 24, octave: float = 2.0, which: str = 'both', lucas_seed: Sequence[int] = (2, 1))[source]#
Bases:
objectIdentify which mathematical series best matches a biosignal’s ratios.
Accepts a fitted
compute_biotunerinstance or aHarmonicInput, extracts a list of octave-folded peak ratios, and scores each candidate series by the proportion of biosignal ratios it reproduces (under a max-denominator rational match). The matched subset of the best series can then be turned into a scale (series_scale()) or a consonance-selected mode (series_mode()).- Parameters:
source (compute_biotuner or HarmonicInput, optional) – The biosignal descriptor. Mutually exclusive with
ratios.ratios (list of float, optional) – Peak ratios supplied directly (mainly for testing). Used when
sourceis None.ratios_source (str, default=’peaks_ratios’) – Which scale to read off
source. For acompute_biotunerthis is an attribute name (e.g."peaks_ratios"or"extended_peaks_ratios"). For aHarmonicInputit is looked up inratios_alternates, falling back to the canonical ratios.series_names (sequence of str, optional) – Series to compare. Defaults to
DEFAULT_SERIES.order (int, default=20) – Number of terms generated per series (Farey order for
"farey").maxdenom (int, default=24) – Maximum denominator of the rational match. Lower = more lenient (nearby ratios collapse onto simple fractions), higher = stricter. Useful discrimination typically lives in roughly
16–48: below that the small fraction grid saturates (every series matches), above it full-precision peak ratios rarely snap onto any simple fraction.octave (float, default=2.0) – Period the ratios are folded into.
which ({‘both’, ‘high/low’, ‘low/high’}, default=’both’) – Pair direction passed to
series_ratio_pairs().lucas_seed (sequence of int, default=(2, 1)) – Starting pair for the Lucas series.
- ratios#
The extracted biosignal peak ratios (octave-folded, in
[1, octave)).- Type:
list of float
- series_scores#
Per-series result dict (populated by
analyze()). Keys per series:proportion,proportion_normalized,n_matched,n_target,n_series_ratios,matched_keys,matched_series_pairs,matched_target_ratios.- Type:
dict
- best_series#
Name of the series with the highest raw proportion.
- Type:
str or None
Examples
>>> ms = math_series(ratios=[1.5, 1.6, 1.25, 1.333], series_names=["fibonacci", "harmonics"], maxdenom=16) >>> ms.analyze().best_series in {"fibonacci", "harmonics"} True
- analyze() math_series[source]#
Score every candidate series against the biosignal ratios.
Populates
series_scoresandbest_series. Returnsselfso calls can be chained (math_series(bt).analyze()).
- series_scale(name: str | None = None, include_unison: bool = True) List[float][source]#
Return the matched subset of a series as a sorted scale in
[1, octave).- Parameters:
name (str, optional) – Series to use. Defaults to
best_series.include_unison (bool, default=True) – Prepend
1.0(the unison) if absent.
- Returns:
list of float – Sorted, de-duplicated ratios of the matched series subset.
- series_mode(name: str | None = None, n_steps: int = 7, method: str = 'subset', function: ~typing.Any = <function compute_consonance>) List[float][source]#
Reduce a series scale to an
n_stepsconsonance-selected mode.Wraps the house mode-selection helpers
create_mode()(method="subset") andtuning_reduction()(method="pairwise").- Parameters:
name (str, optional) – Series to use. Defaults to
best_series.n_steps (int, default=7) – Number of steps in the reduced mode (capped at the scale size).
method ({‘subset’, ‘pairwise’}, default=’subset’) – Selection strategy.
"subset"searches all step combinations (best for small scales);"pairwise"greedily adds the most consonant pairs.function (callable, default=compute_consonance) – Consonance metric. One of
compute_consonance(),dyad_similarity(),metric_denom()(or pass the string name).
- Returns:
list of float – The mode, sorted ascending.
- scale_cents(name: str | None = None) List[float][source]#
Return
series_scale()expressed in cents.
- plot_proportions(normalized: bool = True, ax: Axes | None = None, plot: bool = True, save: bool = False, savename: str = 'series_proportions') Figure[source]#
Bar chart of the match proportion per series.
- Parameters:
normalized (bool, default=True) – Plot proportions normalised to sum to 1 (across the compared series) rather than raw proportions.
ax (matplotlib.axes.Axes, optional) – Draw onto an existing axis (for composing multi-panel figures). A new figure is created when omitted.
plot (bool, default=True) – Call
plt.show()(ignored whenaxis supplied).save (bool, default=False) – Save the figure to
<savename>.png.savename (str, default=’series_proportions’) – File stem used when
saveis True.
- Returns:
matplotlib.figure.Figure
- plot_ratio_pairs(names: Sequence[str] | None = None, ax: Axes | None = None, plot: bool = True, save: bool = False, savename: str = 'series_ratio_pairs') Figure[source]#
Log-log scatter of sequence element pairs, matched pairs highlighted.
Each series’ pairs are drawn as small translucent dots; the pairs whose folded ratio matches a biosignal ratio are overdrawn large with a dark edge (the figure from the notebook).
- Parameters:
names (sequence of str, optional) – Series to draw. Defaults to all compared series.
ax (matplotlib.axes.Axes, optional) – Draw onto an existing axis (for composing multi-panel figures). A new figure is created when omitted.
plot, save, savename – As in
plot_proportions().
- Returns:
matplotlib.figure.Figure
- plot_octave_wheel(order: int | None = None, ax: Axes | None = None, plot: bool = True, save: bool = False, savename: str = 'series_octave_wheel') Figure[source]#
Octave wrapped to a circle (angle = cents): a ratio-ring per series, the signal peaks as spokes, and a filled dot where a spoke matches.
orderoverrides the instance order for display only (thins the rings).
- plot_cents_ruler(order: int | None = None, ax: Axes | None = None, plot: bool = True, save: bool = False, savename: str = 'series_cents_ruler') Figure[source]#
Each series as a lane of ratio-ticks on a 0-1200 cents axis (bold = matched); guide lines drop from each signal peak.
- plot_fit_landscape(order: int | None = None, ax: Axes | None = None, plot: bool = True, save: bool = False, savename: str = 'series_fit') Figure[source]#
For each signal peak, the cents distance to the nearest ratio of each series (lower = the series hugs the spectrum more tightly).
- plot_simplicity_bubbles(order: int | None = None, ax: Axes | None = None, plot: bool = True, save: bool = False, savename: str = 'series_simplicity_bubbles') Figure[source]#
Each series ratio as a bubble sized by simplicity (bigger = smaller denominator); outlined bubbles are matched. Shows whether the signal peaks land on simple rungs.
- plot_series_comb(peaks_hz: Sequence[float] | None = None, fmin: float = 2.0, fmax: float = 45.0, order: int | None = None, ax: Axes | None = None, plot: bool = True, save: bool = False, savename: str = 'series_comb') Figure[source]#
Each series as an across-octave frequency comb (scaled to best fit the signal); the signal peaks (Hz) snap onto the nearest comb step, and each lane is labelled with the mean miss in cents.
Needs peak frequencies: uses
peaks_hz(captured from a biotuner / HarmonicInput) or an explicitpeaks_hz.