Generator embedding optimization

From Xenharmonic Wiki
Revision as of 22:54, 27 October 2026 by Sintel (talk | contribs) (Prepare for split)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

When optimizing tunings of regular temperaments, it is fairly quick and easy to find approximate solutions, using (for example) the general method which is discussed in D&D's guide to RTT and available in D&D's RTT library in Wolfram Language. This RTT library also includes four other methods which quickly and easily find exact solutions. These four methods are further different from the general method insofar as they are not general; each one works only for certain optimization problems. It is these four specialized exact-solution methods which are the subject of this article.

Two of these four specialized methods were briefly discussed in D&D's guide, along with the general method, because these specialized methods are actually even quicker and easier than the general method. These two are the only held-intervals method, and the pseudoinverse method. But there's still plenty more insight to be had into how and why exactly these methods work, in particular for the pseudoinverse method, so we'll be doing a much deeper dive into it in this article here than was done in D&D's guide.

The other two of these four specialized methods—the zero-damage method, and the coinciding-damage method—are significantly more challenging to understand than the general method. Most students of RTT would not gain enough musical insight by familiarizing themselves with them to have justified the investment. This is why these two methods were not discussed in D&D's guide. However, if you feel compelled to understand the nuts and bolts of these methods anyway, then those sections of the article may well appeal to you.

This article is titled "Generator embedding optimization" because of a key feature these four specialized methods share: they can all give their solutions as generator embeddings, i.e. lists of prime-count vectors, one for each generator, where typically these prime-count vectors have non-integer entries (and are thus not JI). This is different from the general method, which can only give generator tuning maps, i.e. sizes in cents for each generator. As we'll see, a tuning optimization method's ability to give solutions as generator embeddings is equivalent to its ability to give solutions that are exact.

Intro

A summary of the methods

The three main chapters of this article are dedicated to three specialized tuning methods, one for each of the three special optimization powers: the pseudoinverse method is used for [math]\displaystyle{ p = 2 }[/math] (miniRMS tuning schemes), the zero-damage method is used for [math]\displaystyle{ p = 1 }[/math] (miniaverage tuning schemes), and the coinciding-damage method is used for [math]\displaystyle{ p = ∞ }[/math] (minimax tuning schemes).

These three methods also work for all-interval tuning schemes, which by definition are all minimax tuning schemes (optimization power [math]\displaystyle{ ∞ }[/math]), differing instead by the power of the power norm used for the interval complexity by which they simplicity-weight damage. But it's not the interval complexity norm power [math]\displaystyle{ q }[/math] which directly determines the method used, but rather its dual power, [math]\displaystyle{ \text{dual}(q) }[/math]: the power of the dual norm minimized on the retuning magnitude. So the pseudoinverse method is used for [math]\displaystyle{ \text{dual}(q) = 2 }[/math], the zero-damage method is used for [math]\displaystyle{ \text{dual}(q) = 1 }[/math], and the coinciding-damage method is used for [math]\displaystyle{ \text{dual}(q) = ∞ }[/math].

If for some reason you've decided that you want to use a different optimization power than those three, then no exact solution in the form of a generator embedding is available, and you'll need to fall back to the general tuning computation method, linked above.

The general method also works for those special powers [math]\displaystyle{ 1 }[/math], [math]\displaystyle{ 2 }[/math], and [math]\displaystyle{ ∞ }[/math], however, so if you're in a hurry, you should skip this article and lean on that method instead (though you should be aware that the general method offers less insight about each of those tuning schemes than their specialized methods do).

Exact vs. approximate solutions

Tuning computation methods can be classified by whether they give an approximate or exact solution.

The general method is an approximate type; it finds the generator tuning map [math]\displaystyle{ 𝒈 }[/math] directly, using trial-and-error methods such as gradient descent or differential evolution whose details we won't go into. The accuracy of approximate types depends on how long you are willing to wait.

In contrast, the exact type work by solving for a matrix [math]\displaystyle{ G }[/math], the generator embedding.

We can calculate [math]\displaystyle{ 𝒈 }[/math] from this [math]\displaystyle{ G }[/math] via [math]\displaystyle{ 𝒋G }[/math], that is, the generator tuning map is obtained as the product of the just tuning map and the generator embedding.

Because [math]\displaystyle{ 𝒈 = 𝒋G }[/math], if [math]\displaystyle{ 𝒈 }[/math] is the primary target, not [math]\displaystyle{ G }[/math], and a formula for [math]\displaystyle{ G }[/math] is known, then it is possible to substitute that into [math]\displaystyle{ 𝒈 = 𝒋G }[/math] and thereby bypass explicitly solving for [math]\displaystyle{ G }[/math]. For example, this was essentially what was done in the Only-held intervals method and Pseudoinverse method sections of D&D's guide: Tuning computation).

Note that with any exact type that solves for [math]\displaystyle{ G }[/math], since it is possible to have an exact [math]\displaystyle{ 𝒋 }[/math], it is also possible to find an exact [math]\displaystyle{ 𝒈 }[/math]. For example, the approximate value of the 5-limit [math]\displaystyle{ 𝒋 }[/math] we're quite familiar with is ⟨1200.000 1901.955 2786.314], but its exact value is ⟨[math]\displaystyle{ 1200×\log_2(2) }[/math] [math]\displaystyle{ 1200×\log_2(3) }[/math] [math]\displaystyle{ 1200×\log_2(5) }[/math]], so if the exact tuning of quarter-comma meantone is [math]\displaystyle{ G }[/math] = {[1 0 0⟩ [0 0 ¼⟩], then this can be expressed as an exact generator tuning map [math]\displaystyle{ 𝒈 }[/math] = {[math]\displaystyle{ (1200×\log_2(2))(1) + (1200×\log_2(3))(0) + (1200×\log_2(5))(0) }[/math] [math]\displaystyle{ (1200×\log_2(2))(0) + (1200×\log_2(3))(0) + (1200×\log_2(5))(\frac14) }[/math]] = {[math]\displaystyle{ 1200 }[/math] [math]\displaystyle{ \dfrac{1200×\log_2(5)}{4} }[/math]].

Also note that any method which solves for [math]\displaystyle{ G }[/math] can also produce [math]\displaystyle{ 𝒈 }[/math] via this [math]\displaystyle{ 𝒋G }[/math] formula. But methods which solve directly for [math]\displaystyle{ 𝒈 }[/math] cannot provide a [math]\displaystyle{ G }[/math], even if a [math]\displaystyle{ G }[/math] could have been computed for the given type of optimization problem (such as a minimax type, which notably is the majority of tuning optimizations used on the wiki). In a way, tuning maps are like a lossily compressed form of information from embeddings.

Here's a breakdown of which computation methods solve directly for [math]\displaystyle{ 𝒈 }[/math], and which can solve for [math]\displaystyle{ G }[/math] instead:

Optimization power Method Solution type Solves for
[math]\displaystyle{ 2 }[/math] Pseudoinverse Exact [math]\displaystyle{ G }[/math]
[math]\displaystyle{ 1 }[/math] Zero-damage Exact [math]\displaystyle{ G }[/math]
[math]\displaystyle{ ∞ }[/math] Coinciding-damage Exact [math]\displaystyle{ G }[/math]
General Power Approximate [math]\displaystyle{ 𝒈 }[/math]
power limit
N/A Only held-intervals Exact [math]\displaystyle{ G }[/math]

The generator embedding

Roughly speaking, if [math]\displaystyle{ M }[/math] is the matrix which isolates the temperament information, and [math]\displaystyle{ 𝒋 }[/math] is the matrix which isolates the sizing information, then [math]\displaystyle{ G }[/math] is the matrix that isolates the tuning information. This is a matrix whose columns are prime-count vectors representing the generators of the temperament. For example, a Pythagorean tuning of meantone temperament would look like this:


[math]\displaystyle{ G = \left[ \begin{array} {rrr} 1 & {-1} \\ 0 & 1 \\ 0 & 0 \\ \end{array} \right] }[/math]


The first column is the vector [1 0 0⟩ representing [math]\displaystyle{ \frac21 }[/math], and the second column is the vector [-1 1 0⟩ representing [math]\displaystyle{ \frac32 }[/math]. So generator embeddings will always have the shape [math]\displaystyle{ (d, r) }[/math]: one row for each prime harmonic in the domain basis (the dimensionality), one column for each generator (the rank).

Pythagorean tuning is not a common tuning of meantone, however, and is an extreme enough tuning of that temperament that it should be considered unreasonable. We gave it as our first example anyway, though, in order to more gently introduce the concept of generator embeddings, because its prime-count vector columns are simple and familiar, while in reality, most generator embeddings consist of prime-count vectors which do not have integer entries. Therefore, these prime-count vectors do not represent JI intervals, and are unlike any prime-count vectors we've worked with so far. For another example of a meantone tuning, then, one which is more common and reasonable, let's consider the quarter-comma tuning of meantone. Its generator embedding looks like this:


[math]\displaystyle{ G = \left[ \begin{array} {rrr} 1 & 0 \\ 0 & 0 \\ 0 & \frac14 \\ \end{array} \right] }[/math]


Algebraic setup

The basic algebraic setup of tuning optimization looks like this:


[math]\displaystyle{ \textbf{d} = |\,𝒈M\mathrm{T}W - 𝒋\mathrm{T}W\,| }[/math]


When we break [math]\displaystyle{ 𝒈 }[/math] down into [math]\displaystyle{ 𝒋 }[/math] and a [math]\displaystyle{ G }[/math] we're solving for, the algebraic setup of tuning optimization comes out like this:


[math]\displaystyle{ \textbf{d} = |\,𝒋GM\mathrm{T}W - 𝒋G_{\text{j}}M_{\text{j}}\mathrm{T}W\,| }[/math]


We can factor things in both directions this time (and we'll take [math]\displaystyle{ 𝒋 }[/math] outside the absolute value bars since it's guaranteed to have no negative entries):


[math]\displaystyle{ \textbf{d} = 𝒋\,|\,(GM - G_{\text{j}}M_{\text{j}})\mathrm{T}W\,| }[/math]


But wait—there are actually two more matrices we haven't recognized yet, on the just side of things. These are [math]\displaystyle{ G_{\text{j}} }[/math] and [math]\displaystyle{ M_{\text{j}} }[/math]. Unsurprisingly, these two are closely related to [math]\displaystyle{ G }[/math] and [math]\displaystyle{ M }[/math], respectively. The subscript [math]\displaystyle{ \text{j} }[/math] stands for "just intonation", so this is intended to indicate that these are the generators and mapping for JI.

We could replace either or both of these matrices with [math]\displaystyle{ I }[/math], an identity matrix. On account of both [math]\displaystyle{ G_{\text{j}} }[/math] and [math]\displaystyle{ M_{\text{j}} }[/math] being identity matrices, we can eliminate them from our expression


[math]\displaystyle{ \textbf{d} = 𝒋\,|\,(GM - II)\mathrm{T}W\,| }[/math]


Which reduces to:


[math]\displaystyle{ \textbf{d} = 𝒋\,|\,(P - I)\mathrm{T}W\,| }[/math]


Where [math]\displaystyle{ P }[/math] is the projection matrix found as [math]\displaystyle{ P = GM }[/math].

So why do we have [math]\displaystyle{ G_{\text{j}} }[/math] and [math]\displaystyle{ M_{\text{j}} }[/math] there at all? For maximal parallelism between the tempered side and the just side. In part this is a pragmatic decision, because as we work with these sorts of expressions moving forward, we'll prefer something rather than nothing in this position anyway. But there's also a pedagogical goal here, which is to convey how in JI, the mapping matrix and the generator embedding really are identity matrices, and it can be helpful to stay mindful of it.

You can imagine reading a [math]\displaystyle{ (3, 3) }[/math]-shaped identity matrix like a mapping matrix: how many generators does it take to approximate prime 2? One of the first generator, and nothing else. How many to approximate prime 3? One of the second generator, and nothing else. How many to approximate prime 5? One of the third generator, and nothing else. So this mapping is not much of a mapping at all. It shows us only that in this temperament, the first generator may as well be a perfect approximation of prime 2, the second generator may as well be a perfect approximation of prime 3, and the third generator may as well be a perfect approximation of prime 5. Any temperament which has as many generators as it has primes may as well be JI like this.

And then the fact that the generator embedding on the just side is also an identity matrix finishes the point. The vector for the first generator is [1 0 0⟩, a representation of the interval [math]\displaystyle{ \frac21 }[/math]; the vector for the second generator is [0 1 0⟩, a representation of the interval [math]\displaystyle{ \frac31 }[/math]; and the vector for the third generator is [0 0 1⟩, a representation of the interval [math]\displaystyle{ \frac51 }[/math].

We can even understand this in terms of a units analysis, where if [math]\displaystyle{ M_{\text{j}} }[/math] is taken to have units of g/p, and [math]\displaystyle{ G_{\text{j}} }[/math] is taken to have units of p/g, then together we find their units to be ... nothing. And an identity matrix that isn't even understood to have units is definitely useless and to be eliminated. Though it's actually not as simple as the [math]\displaystyle{ \small \sf p }[/math]'s and [math]\displaystyle{ \small \sf g }[/math]'s canceling out; for more details, see here.

So when the interval vectors constituting the target-interval list [math]\displaystyle{ \mathrm{T} }[/math] are multiplied by [math]\displaystyle{ G_{\text{j}}M_{\text{j}} }[/math] they are unchanged, which means that multiplying the result by [math]\displaystyle{ 𝒋 }[/math] simply computes their just sizes.

Deduplication

Between target-interval set and held-interval basis

Generally speaking, held-intervals should be removed if they also appear in the target-interval set. If these intervals are not removed, the correct tuning can still be computed; however, during optimization, effort will have been wasted on minimizing damage to these intervals, because their damage would have been held to 0 by other means anyway.

Of course, there is some cost to the deduplication itself, but In general, it should be more computationally efficient to remove these intervals from the target-interval set in advance, rather than submit them to the optimization procedures as-is.

Duplication of intervals between these two sets will most likely occur when using a target-interval set scheme (such as a TILT or OLD) that automatically chooses the target-interval set.

Constant damage target-intervals

There is also a possibility, when holding intervals, that some target-intervals' damages will be constant everywhere within the tuning damage space to be searched, and thus these target-intervals will have no effect on the tuning. Their preservation in the target-interval set will only serve to slow down computation.

For example, in pajara temperament, with mapping [⟨2 3 5 6] ⟨0 1 -2 -2]}, if the octave is held unchanged, then there is no sense keeping [math]\displaystyle{ \frac75 }[/math] in the target-interval set. The octave [1 0 0 0⟩ maps to [2 0} in this temperament, and [math]\displaystyle{ \frac75 }[/math] [0 0 -1 1⟩ maps to [1 0}. So if the first generator is fixed in order to hold the octave unchanged, then ~[math]\displaystyle{ \frac75 }[/math]'s tuning will also be fixed.

Within target-interval set

We also note a potential for duplication within the target-interval set, irrespective of held-intervals: depending on the temperament, some target-intervals may map to the same tempered interval. For another pajara example, using the TILT as a target-interval set scheme, the target-interval set will contain [math]\displaystyle{ \frac{10}{7} }[/math] and [math]\displaystyle{ \frac75 }[/math], but pajara maps both of those intervals to [1 0}, and thus the damage to these two intervals will always be the same.

However, critically, this is only truly redundant information in the case of a minimax tuning scheme, where the optimization power [math]\displaystyle{ p = ∞ }[/math]. In this case, if the damage to [math]\displaystyle{ \frac75 }[/math] is the max, then it's irrelevant whether the damage to [math]\displaystyle{ \frac{10}{7} }[/math] is also the max. But in the case of any other optimization power, both the presence of [math]\displaystyle{ \frac75 }[/math] and of [math]\displaystyle{ \frac{10}{7} }[/math] in the target-interval set will have some effect; for example, with [math]\displaystyle{ p = 1 }[/math], miniaverage tuning schemes, this means that whatever the identical damage to this one mapped target-interval [1 0} may be, since two different of our target-intervals map to it, we care about its damage twice as much, and thus it essentially gets counted twice in our average damage computation.

Should redundant mapped target-intervals be removed when computing minimax tuning schemes? It's a reasonable consideration. The RTT Library in Wolfram Language does not do this. In general, this may add more complexity to the code than the benefit is worth; it requieres minding the difference between the requested target-interval set count [math]\displaystyle{ k }[/math] and the count of deduped mapped target-intervals, which would require a new variable.

Only held-intervals method

The only held-intervals method was mostly covered here: Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Only held-intervals method. But there are a couple adjustments we'll make to how we talk about it here.

Unchanged-interval basis

In the D&D's guide article, this method was discussed in terms of held-intervals, which are a trait of a tuning scheme, or in other words, a request that a person makes of a tuning optimization procedure which that procedure will then satisfy. But there's something interesting that happens once we request enough many intervals to be held unchanged—that is, when our held-interval count [math]\displaystyle{ h }[/math] reaches the size of our generator count, also known as rank [math]\displaystyle{ r }[/math]—then we have no room left for optimization. At this point, the tuning is entirely determined by the held-intervals. And thus we get another, perhaps better, way to look at the interval basis: no longer in terms of a request on a tuning scheme, but as a characteristic of a specific tuning itself. Under this conceptualization, what we have is not a helf-interval basis [math]\displaystyle{ \mathrm{H} }[/math], but an unchanged-interval basis [math]\displaystyle{ \mathrm{U} }[/math].

Because in the majority of cases within this article it will be more appropriate to conceive of this basis as a characteristic of a fully-determined tuning, as opposed to a request of tuning scheme, we will be henceforth be dealing with this method in terms of [math]\displaystyle{ \mathrm{U} }[/math], not [math]\displaystyle{ \mathrm{H} }[/math]

Generator embedding

So, substituting [math]\displaystyle{ \mathrm{U} }[/math] in for [math]\displaystyle{ \mathrm{H} }[/math] in the formula we learned from the D&D's guide article:


[math]\displaystyle{ 𝒈 = 𝒋\mathrm{U}(M\mathrm{U})^{-1} }[/math]


This tells us that if we know the unchanged-interval basis for a tuning, i.e. every unchanged-interval in the form of a prime-count vector, then we can get our generators. But the next difference we want to look at here is this: the formula has bypassed the computation of [math]\displaystyle{ G }[/math]! We can expand [math]\displaystyle{ 𝒈 }[/math] to [math]\displaystyle{ 𝒋G }[/math]:


[math]\displaystyle{ 𝒋G = 𝒋\mathrm{U}(M\mathrm{U})^{-1} }[/math]


And cancel out:


[math]\displaystyle{ \cancel{𝒋}G = \cancel{𝒋}\mathrm{U}(M\mathrm{U})^{-1} }[/math]


To find:


[math]\displaystyle{ G = \mathrm{U}(M\mathrm{U})^{-1} }[/math]

Pseudoinverse method

The pseudoinverse method gives exact solutions for miniRMS tuning schemes (optimization power [math]\displaystyle{ p = 2 }[/math]), and for all-interval tuning schemes with [math]\displaystyle{ \text{dual}(q) = 2 }[/math].

Zero-damage method

The zero-damage method gives exact solutions for miniaverage tuning schemes (optimization power [math]\displaystyle{ p = 1 }[/math]), and for all-interval tuning schemes with [math]\displaystyle{ \text{dual}(q) = 1 }[/math].

Coinciding-damage method

The coinciding-damage method gives exact solutions for minimax tuning schemes (optimization power [math]\displaystyle{ p = ∞ }[/math]), and for all-interval tuning schemes with [math]\displaystyle{ \text{dual}(q) = ∞ }[/math]. How it breaks ties between non-unique minimax tunings is covered in Coinciding-damage tie-breaking.

Alternative complexities

All three methods can also be used with complexities other than log-product complexity; worked examples for each are collected in Alternative complexities.