Brun's algorithm: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Naren (talk | contribs)
Add Brentjes book to Further reading
Naren (talk | contribs)
Expand on scale construction method
Line 181: Line 181:


== Other applications ==
== Other applications ==
[[File:Wilson-Brun.jpeg|thumb|300px|Figure 1: Wilson's scale construction using Brun's algorithm]]
[[Erv Wilson]] used the intervals generated by Brun's algorithm to construct scales.<ref name="wilson1995brun"/>
[[Erv Wilson]] used the intervals generated by Brun's algorithm to construct scales.<ref name="wilson1995brun"/>
The method starts with a one note scale consisting of the largest interval you are approximating.
Then, writing <math>a</math> and <math>b</math> for the first two elements of the cents vector <math>A</math>,
each time you compute <math>a - b</math>, you split every interval <math>a</math> in your scale into <math>b</math> and <math>a - b</math> (in either order).
This gives a sequence of scales in which each contains the previous one.
The steps in each scale are given by the elements of the cents vector <math>A</math> in the corresponding iteration of Brun's algorithm,
and their counts are given by the first row of the matrix <math>X</math>.
Figure 1 shows this process for Brun's algorithm applied to 2/1, 3/2, and 5/4.<ref name="wilson1995brun"/>
The [https://apps.apple.com/us/app/wilsonic/id848852071 Wilsonic iOS app] implements scale construction using Brun's method.
To choose the splitting order, it uses the heuristic of splitting interval <math>a</math> into <math>[b, a - b]</math>  unless the previous step was <math>b</math>, in which case it uses the other order <math>[a - b, b]</math>.<ref name="wilsonic-brun"/>
Kraig Grady has applied Brun's algorithm analogously to rhythm.<ref name="grady2011applying"/>
He emphasises that the step ordering involves an element of choice:
<blockquote>
Unlike Moments of Symmetry patterns, the order in which to place the dividing numbers it is not always clear.
Evenness is the key. Best to watch that the shortest duration is flanked by the largest when possible.
One ideally wants all composite durations in a row to lie within a close range without overlapping those higher or lower.
</blockquote>


== References ==
== References ==
Line 193: Line 213:
<ref name="wilson1995brun">
<ref name="wilson1995brun">
[[Erv Wilson]], [https://www.anaphoria.com/viggo3.pdf A Sequence of Constant Structures as Predicted by Brun's Algorithm]. 1995.
[[Erv Wilson]], [https://www.anaphoria.com/viggo3.pdf A Sequence of Constant Structures as Predicted by Brun's Algorithm]. 1995.
</ref>
<ref name="wilsonic-brun>
[https://github.com/marcus-w-hobbs/Wilsonic-MTS-ESP/blob/99c17cc1a8cb5328330c6c0a63b1b0f1be3981b8/experiments/lattice/brun3.py Python version of the Wilsonic iOS Brun implementation in the Wilsonic github repo]
</ref>
<ref name="grady2011applying">
[[Kraig Grady]], [https://anaphoria.com/ViggoRhythm.pdf Applying Viggo Brun's Algorithm to Rhythm]. 2011.
</ref>
</ref>
</references>
</references>

Revision as of 07:56, 21 October 2026

Brun's algorithm is an extension of continued fractions to more than one number.[1][2] It can be used to find EDOs that contain good approximations to given just ratios.

Algorithm

Say we would like to find EDOs which have good approximations of 5/4, 3/2, and 2/1. First, write the corresponding cents values, largest to smallest, in a vector [math]\displaystyle{ A_{0} }[/math],

\begin{displaymjx} A_{0} = \begin{bmatrix} 1200.00 \\ 701.96 \\ 386.31 \end{bmatrix} \end{displaymjx}

Now write [math]\displaystyle{ XA = A_{0} }[/math], where to begin with [math]\displaystyle{ X }[/math] is the identity matrix and [math]\displaystyle{ A = A_{0} }[/math],

\begin{displaymjx} \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} 1200.00 \\ 701.96 \\ 386.31 \end{bmatrix} \\ = \begin{bmatrix} 1200.00 \\ 701.96 \\ 386.31 \end{bmatrix} \end{displaymjx}

Each iteration of the algorithm transforms [math]\displaystyle{ X }[/math] and [math]\displaystyle{ A }[/math] such that

  • We preserve [math]\displaystyle{ X A = A_{0} }[/math]
  • The elements of [math]\displaystyle{ A }[/math] get smaller
  • The first column of [math]\displaystyle{ X }[/math] gives a better EDO approximation of [math]\displaystyle{ A_{0} }[/math]

To do one iteration of the algorithm, firstly subtract the second element from the first element in the cents vector [math]\displaystyle{ A }[/math] and add the first column to the second column in the matrix [math]\displaystyle{ X }[/math], giving

\begin{displaymjx} \begin{bmatrix} 1 & 0 + 1 & 0 \\ 0 & 1 + 0 & 0 \\ 0 & 0 + 0 & 1 \end{bmatrix} \begin{bmatrix} 1200.00 - 701.96 \\ 701.96 \\ 386.31 \end{bmatrix} = \begin{bmatrix} 1 & 1 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} 498.04 \\ 701.96 \\ 386.31 \end{bmatrix} \end{displaymjx}

Secondly, sort the cents vector [math]\displaystyle{ A }[/math] and rearrange the columns of [math]\displaystyle{ X }[/math] in the corresponding order, giving

\begin{displaymjx} \begin{bmatrix} 1 & 1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} 701.96 \\ 498.04 \\ 386.31 \end{bmatrix} \end{displaymjx}

Repeating this process gives:

[math]\displaystyle{ X }[/math] [math]\displaystyle{ A }[/math] Ratios
[math]\displaystyle{ \begin{bmatrix}1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}1200.00 \\ 701.96 \\ 386.31\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}2/1 \\ 3/2 \\ 5/4\end{bmatrix} }[/math]
[math]\displaystyle{ \begin{bmatrix}1 & 1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 1\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}701.96 \\ 498.04 \\ 386.31\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}3/2 \\ 4/3 \\ 5/4\end{bmatrix} }[/math]
[math]\displaystyle{ \begin{bmatrix}2 & 0 & 1 \\ 1 & 0 & 1 \\ 0 & 1 & 0\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}498.04 \\ 386.31 \\ 203.91\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}4/3 \\ 5/4 \\ 9/8\end{bmatrix} }[/math]
[math]\displaystyle{ \begin{bmatrix}2 & 1 & 2 \\ 1 & 1 & 1 \\ 1 & 0 & 0\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}386.31 \\ 203.91 \\ 111.73\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}5/4 \\ 9/8 \\ 16/15\end{bmatrix} }[/math]
[math]\displaystyle{ \begin{bmatrix}3 & 2 & 2 \\ 2 & 1 & 1 \\ 1 & 1 & 0\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}203.91 \\ 182.40 \\ 111.73\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}9/8 \\ 10/9 \\ 16/15\end{bmatrix} }[/math]
[math]\displaystyle{ \begin{bmatrix}5 & 2 & 3 \\ 3 & 1 & 2 \\ 2 & 0 & 1\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}182.40 \\ 111.73 \\ 21.51\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}10/9 \\ 16/15 \\ 81/80\end{bmatrix} }[/math]
[math]\displaystyle{ \begin{bmatrix}7 & 5 & 3 \\ 4 & 3 & 2 \\ 2 & 2 & 1\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}111.73 \\ 70.67 \\ 21.51\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}16/15 \\ 25/24 \\ 81/80\end{bmatrix} }[/math]
[math]\displaystyle{ \begin{bmatrix}12 & 7 & 3 \\ 7 & 4 & 2 \\ 4 & 2 & 1\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}70.67 \\ 41.06 \\ 21.51\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}25/24 \\ 128/125 \\ 81/80\end{bmatrix} }[/math]
[math]\displaystyle{ \begin{bmatrix}19 & 12 & 3 \\ 11 & 7 & 2 \\ 6 & 4 & 1\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}41.06 \\ 29.61 \\ 21.51\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}128/125 \\ 3125/3072 \\ 81/80\end{bmatrix} }[/math]
[math]\displaystyle{ \begin{bmatrix}31 & 3 & 19 \\ 18 & 2 & 11 \\ 10 & 1 & 6\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}29.61 \\ 21.51 \\ 11.45\end{bmatrix} }[/math] [math]\displaystyle{ \begin{bmatrix}3125/3072 \\ 81/80 \\ 393216/390625\end{bmatrix} }[/math]

Here each row shows one iteration of the algorithm. The Ratios column shows the ratios corresponding to the cents values in [math]\displaystyle{ A }[/math] (from dividing ratios each time we subtract cents values).

The first column of each matrix [math]\displaystyle{ X }[/math] gives an EDO approximation to [math]\displaystyle{ A_{0} }[/math]. For example, the matrix with first column [math]\displaystyle{ [31, 18, 10] }[/math] gives

\begin{displaymjx} \begin{aligned} \frac{1200}{31} \begin{bmatrix} 31 \\ 18 \\ 10 \end{bmatrix} &= \begin{bmatrix} 1200.00 \\ 696.77 \\ 387.10 \end{bmatrix} \\ &\approx \begin{bmatrix} 1200.00 \\ 701.96 \\ 386.31 \end{bmatrix} \end{aligned} \end{displaymjx}

Code

The following Python code implements Brun's algorithm.

import numpy as np

target_ratios = [5/4, 3/2, 2/1]

A = 1200 * np.log2(sorted(target_ratios, reverse=True))
X = np.identity(len(A), dtype=np.int64)

while X.max() <= 100:
    print(f"{X}\n{A}\n")
    A[0] -= A[1]
    X[:, 1] += X[:, 0]
    i = np.argsort(-A)
    A = A[i]
    X = X[:, i]

Other applications

Figure 1: Wilson's scale construction using Brun's algorithm

Erv Wilson used the intervals generated by Brun's algorithm to construct scales.[3] The method starts with a one note scale consisting of the largest interval you are approximating. Then, writing [math]\displaystyle{ a }[/math] and [math]\displaystyle{ b }[/math] for the first two elements of the cents vector [math]\displaystyle{ A }[/math], each time you compute [math]\displaystyle{ a - b }[/math], you split every interval [math]\displaystyle{ a }[/math] in your scale into [math]\displaystyle{ b }[/math] and [math]\displaystyle{ a - b }[/math] (in either order). This gives a sequence of scales in which each contains the previous one. The steps in each scale are given by the elements of the cents vector [math]\displaystyle{ A }[/math] in the corresponding iteration of Brun's algorithm, and their counts are given by the first row of the matrix [math]\displaystyle{ X }[/math]. Figure 1 shows this process for Brun's algorithm applied to 2/1, 3/2, and 5/4.[3]

The Wilsonic iOS app implements scale construction using Brun's method. To choose the splitting order, it uses the heuristic of splitting interval [math]\displaystyle{ a }[/math] into [math]\displaystyle{ [b, a - b] }[/math] unless the previous step was [math]\displaystyle{ b }[/math], in which case it uses the other order [math]\displaystyle{ [a - b, b] }[/math].[4]

Kraig Grady has applied Brun's algorithm analogously to rhythm.[5] He emphasises that the step ordering involves an element of choice:

Unlike Moments of Symmetry patterns, the order in which to place the dividing numbers it is not always clear. Evenness is the key. Best to watch that the shortest duration is flanked by the largest when possible. One ideally wants all composite durations in a row to lie within a close range without overlapping those higher or lower.

References

Further reading