Fractal#
Fractal-flavoured harmonic geometries.
Phase 4 ships the deterministic group:
stern_brocot_tree()— the canonical mediant tree of all positive rationals, annotated with biotuner harmonicity scores.continued_fraction_rectangles()— the “Euclid-algorithm” recursive square / rectangle subdivision of a ratio.farey_sequence_layout()— the Farey sequence of ordernplaced on a circle or line.subharmonic_tree()— recursive subharmonic expansion of an input usingbiotuner.metrics.compute_subharmonics.ifs_harmonic()— chaos-game iterated-function-system attractor whose contractions are derived from the input ratios.
The generative group (L-systems, harmonic Julia sets, recursive polygons, Cantor rhythms, self-similar tunings) lands in Phase 5.
- stern_brocot_tree(input: HarmonicInput | None = None, max_depth: int = 6, layout: str = 'hyperbolic') GeometryData[source]#
Stern-Brocot mediant tree to
max_depthlevels.Starts from the canonical bounds
0/1and1/0; each node is the mediant of its bracketing pair. The tree at depthdhas exactly2^d - 1interior nodes (the bounds are excluded from the output).Each node is annotated with a harmonicity score in
metadata['harmonicity']— by defaultdyad_similarity(p/q). Ifinputis provided, an additionalmetadata['nearest_input_dist_cents']array records the cents distance from each tree node to the closest ratio ininput, helpful for highlighting where the chord lives in the rational lattice.- Parameters:
input (HarmonicInput, optional) – If given, used only for the nearest-input-distance annotation.
max_depth (int, default=6) – Tree depth. The number of nodes grows as
2^depth - 1.layout ({‘hyperbolic’, ‘tree’}, default=’hyperbolic’) –
'hyperbolic'places nodes on the Poincaré disk by traversal position and depth;'tree'uses a flat dendrogram layout.
- Returns:
GeometryData –
geom_type='tree'.
- continued_fraction_rectangles(ratio: Fraction | int | float | Tuple[int, int], depth: int = 10) GeometryData[source]#
Recursive Euclid-algorithm square / rectangle decomposition of a ratio.
Visualizes the continued-fraction expansion of
p/q(assumed> 1; smaller values are inverted internally and the output is flagged in metadata). The starting rectangle isp × q; the largest possible squares of sidemin(p, q)are stripped off repeatedly, each time rotating the residual strip by 90°. The sequence of squares is the continued-fraction expansion ofp/q.- Parameters:
ratio (Fraction, int, float, or (int, int))
depth (int, default=10) – Maximum number of squares to record. The full expansion terminates earlier if
p/qis rational.
- Returns:
GeometryData –
geom_type='polygon_set'— one rectangular polygon per square, in original-rectangle units (the bounding rectangle is the unit-area rectangle[0, 1] × [0, q/p]).
- farey_sequence_layout(order: int, layout: str = 'circle') GeometryData[source]#
Farey sequence
F_nplaced on a circle, line, or as Ford circles.- Parameters:
order (int) – Sequence order,
>= 1.layout ({‘circle’, ‘line’, ‘ford’}, default=’circle’) –
'circle'/'line'— points only; weight encodes1 / denominator.'ford'— each fractionp/q ∈ F_nbecomes a circle of radius1 / (2 q²)tangent to the x-axis atx = p/q. Adjacent Farey fractions correspond to tangent Ford circles — the classic visual of the Farey structure.
- Returns:
GeometryData – For
'circle'/'line':geom_type='point_cloud_2d'. For'ford':geom_type='polygon_set'— each entry is a polyline approximation of one Ford circle.metadata['radii']andmetadata['centers']carry the analytic geometry.
- subharmonic_tree(input: HarmonicInput, depth: int = 4, n_harmonics: int = 5, min_freq: float = 0.1, layout: str = 'depth') GeometryData[source]#
Recursive subharmonic expansion as a tree.
Each input peak
fis the root of a sub-tree whose children are its firstn_harmonicssubharmonicsf / 2, f / 3, ..., f / (k + 1). Each child is expanded the same way todepthlevels. Nodes with frequency belowmin_freqare pruned.Notes
The plan originally suggested using
biotuner.metrics.compute_subharmonics, which finds common subharmonics across a chord. That’s a different operation from the per-peak subharmonic series this tree visualizes; we use the classicalf / kdefinition directly.- Parameters:
input (HarmonicInput) – Source peaks for the root level.
depth (int, default=4) – Number of expansion levels below the root.
n_harmonics (int, default=5) – Number of subharmonics per node.
min_freq (float, default=0.1) – Frequencies below this are not expanded further.
layout ({‘depth’, ‘polar’}, default=’depth’) –
'depth'— original dendrogram layout (depth on Y, sorted on X).'polar'— each input peak gets its own angular sector; depth becomes radial distance, so different chords produce visibly different fan-out shapes instead of identical depth-stacks.
- Returns:
GeometryData –
geom_type='tree'.metadata['root_index_per_node']tags every node with its originating root-peak index, useful for colour-coding.
- ifs_harmonic(input: HarmonicInput, n_points: int = 50000, contraction: str = 'ratio_inverse', transient: int = 200, rng: Generator | None = None) GeometryData[source]#
Iterated-function-system attractor driven by harmonic ratios.
Each input ratio defines an affine contraction
z -> z · s_i + v_i, wheres_iis the contraction factor (derived from the ratio percontraction) andv_iis the i-th vertex of an N-gon scaled to the unit disk. The classic chaos game then samples the attractor.- Parameters:
input (HarmonicInput) – Provides the N ratios.
n_points (int, default=50_000)
contraction ({‘ratio_inverse’, ‘log_ratio’, ‘fixed_half’}, default=’ratio_inverse’) –
'ratio_inverse':s_i = 1 / r_i(rebound to< 1).'log_ratio':s_i = 1 / (1 + log(r_i)).'fixed_half':s_i = 0.5for all i (Sierpinski-like).
transient (int, default=200) – Number of warm-up iterations to discard before recording points.
rng (np.random.Generator, optional) – Source of randomness. Default:
np.random.default_rng().
- Returns:
GeometryData –
geom_type='point_cloud_2d'with(n_points, 2)coordinates.