Generator embedding optimization/Coinciding-damage tie-breaking
This chapter continues the coinciding-damage method.
We've noted that the zero-damage method for miniaverage tuning schemes cannot directly handle non-unique methods itself. When it finds a non-unique miniaverage (more than one set of generator tunings cause the same minimum sum damage), the next step is to start over with a different method, specifically, the power-limit method, which can only provide an approximate solution.
On the other hand, the coinciding-damage method discussed here, the one for minimax tuning schemes, does have the built-in ability to find a unique true optimum tuning when the minimax tuning is non-unique. In fact, it has not just one, but two different techniques for tie-breaking when it encounters non-unique minimax tunings:
- Comparing ADSLODs. This stands for "abbreviated descending-sorted list of damage". When we have a tied minimax, we essentially have a tie between the first entries of each candidate tuning's ADSLOD. But we can compare more than just the first entry (though we can't compare all entries; hence the "abbreviated" part). If we can break the tie with any of the remaining ADSLOD entries, then we've just done "'basic tie-breaking"'.
- Repeat iterations. When basic tie-breaking fails, we must perform a whole additional iteration of the entire conciding-damage method, but this time only within a narrowed-down region of tuning damage space. Whenever this happens, we've moved on to advanced tie-breaking. More than one repeat iteration may be necessary before a true unique optimum tuning is found (but a round of basic-tiebreaking will occur before each next more advanced iteration of the method.)
Here's a flow chart giving the overall picture:

Basic tie-breaking example: Setup
Let's just dive into an example.
The example given in the diagrams back in article 3 here and article 6 here were a bit silly. The easiest way to break the tie in this case would be to remove the offending target-interval from the set, since with constant damage, it will not aid in preferring one tuning to another. More natural examples of tied tunings—that cannot be resolved so easily—require 3D tuning damage space. So that's what we'll be looking at here.
Suppose we're doing a minimax-U tuning of blackwood temperament, and our target-interval set is [math]\displaystyle{ \{ \frac21, \frac31, \frac51, \frac65 \} }[/math]. This is a rank-2 temperament, so we're in 3D tuning damage space. The relevant region of its tuning damage space is visualized below. The yellow hyper-V is the damage graph for [math]\displaystyle{ \frac21 }[/math], the blue hyper-V is for [math]\displaystyle{ \frac31 }[/math], the green hyper-V is for [math]\displaystyle{ \frac51 }[/math], and the red hyper-V is for [math]\displaystyle{ \frac65 }[/math].
Because the mapped intervals for [math]\displaystyle{ \frac21 }[/math] and [math]\displaystyle{ \frac31 }[/math] both use only the first generator [math]\displaystyle{ g_1 }[/math], this means that their hyper-V creases on the floor are parallel. A further consequence of this is that their hyper-Vs' line of intersection above the floor is parallel to the zero-damage floor and will never touch it. Said another way, there's no tuning of blackwood temperament where both prime [math]\displaystyle{ \frac21 }[/math] and [math]\displaystyle{ \frac31 }[/math] are tuned pure at the same time. (This can also be understood through the nature of the blackwood comma [math]\displaystyle{ \frac{256}{243} }[/math], which contains only primes 2 and 3.)
So, instead of a unique minimax tuning point, we instead find this line-segment range of valid minimax tunings, bounded by the points on either end where the damage to the other target-intervals exceeds this coinciding damage to primes math>\frac21</math> and [math]\displaystyle{ \frac31 }[/math]. We indicated this range on the diagram above. At this point, looking down on the surface of our max damage graph, we know the true optimum tuning is somewhere along this range. But it's hard to tell exactly where. In order to find it, let's gather ReDPOTICs and STICs, convert them to constraint matrices, and convert those in turn to tunings.
Basic tie-breaking example: Comparing tunings for minimax
With four target-intervals, we have sixteen ReDPOTICs to check, and six STICs to check, for a total of 22 points. Here are their constraints, tunings, and damage lists:
| Constraint matrix | Candidate generator tuning map | Target-interval damage list | |
|---|---|---|---|
| [math]\displaystyle{ K }[/math] | [math]\displaystyle{ 𝒈_i }[/math] | {[math]\displaystyle{ g_1 }[/math] [math]\displaystyle{ g_2 }[/math]] | [math]\displaystyle{ \textbf{d} }[/math] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & +1 \\ +1 & 0 \\ 0 & +1 \\ 0 & 0 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_1 }[/math] | {238.612 2793.254] | [6.940 6.940 6.940 6.940] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & +1 \\ +1 & 0 \\ 0 & -1 \\ 0 & 0 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_2 }[/math] | {238.612 2779.374] | [6.940 6.940 6.940 6.940] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & +1 \\ -1 & 0 \\ 0 & +1 \\ 0 & 0 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_3 }[/math] | {233.985 2816.390] | [30.075 30.075 30.075 30.075] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & +1 \\ -1 & 0 \\ 0 & -1 \\ 0 & 0 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_4 }[/math] | {233.985 2756.240] | [30.075 30.075 30.075 30.075] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & +1 \\ +1 & 0 \\ 0 & 0 \\ 0 & +1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_2 }[/math] | {238.612 2779.374] | [6.940 6.940 6.940 6.940] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & +1 \\ +1 & 0 \\ 0 & 0 \\ 0 & -1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_1 }[/math] | {238.612 2793.254] | [6.940 6.940 6.940 6.940] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & +1 \\ -1 & 0 \\ 0 & 0 \\ 0 & +1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_5 }[/math] | {233.985 2696.090] | [30.075 30.075 90.225 30.075] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & +1 \\ -1 & 0 \\ 0 & 0 \\ 0 & -1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_4 }[/math] | {233.985 2756.240] | [30.075 30.075 30.075 30.075] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & +1 \\ 0 & 0 \\ +1 & 0 \\ 0 & +1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_6 }[/math] | {239.215 2790.240] | [3.923 11.769 3.923 3.923] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & +1 \\ 0 & 0 \\ +1 & 0 \\ 0 & -1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_1 }[/math] | {238.612 2793.254] | [6.940 6.940 6.940 6.940] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & +1 \\ 0 & 0 \\ -1 & 0 \\ 0 & +1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_2 }[/math] | {238.612 2779.374] | [6.940 6.940 6.940 6.940] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & +1 \\ 0 & 0 \\ -1 & 0 \\ 0 & -1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_4 }[/math] | {233.985 2756.240] | [30.075 30.075 30.075 30.075] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} 0 & 0 \\ +1 & +1 \\ +1 & 0 \\ 0 & +1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_7 }[/math] | {238.133 2783.200] | [9.334 3.111 3.111 3.111] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} 0 & 0 \\ +1 & +1 \\ +1 & 0 \\ 0 & -1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_2 }[/math] | {238.612 2779.374] | [6.940 6.940 6.940 6.940] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} 0 & 0 \\ +1 & +1 \\ -1 & 0 \\ 0 & +1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_1 }[/math] | {238.612 2793.254] | [6.940 6.940 6.940 6.940] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} 0 & 0 \\ +1 & +1 \\ -1 & 0 \\ 0 & -1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_4 }[/math] | {233.985 2756.240] | [30.075 30.075 30.075 30.075] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & 0 \\ 0 & +1 \\ 0 & 0 \\ 0 & 0 \\ \end{array} \right] }[/math] | n/a | n/a | n/a |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & 0 \\ 0 & 0 \\ 0 & +1 \\ 0 & 0 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_8 }[/math] | {240.000 2786.314] | [0.000 18.045 0.000 18.045] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} +1 & 0 \\ 0 & 0 \\ 0 & 0 \\ 0 & +1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_9 }[/math] | {240.000 2804.360] | [0.000 18.045 18.045 0.000] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} 0 & 0 \\ +1 & 0 \\ 0 & +1 \\ 0 & 0 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_{10} }[/math] | {237.744 2786.314] | [11.278 0.000 0.000 11.278] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} 0 & 0 \\ +1 & 0 \\ 0 & 0 \\ 0 & +1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_{11} }[/math] | {237.744 2775.040] | [11.278 0.000 11.278 0.000] |
| [math]\displaystyle{ \left[ \begin{array} {rrr} 0 & 0 \\ 0 & 0 \\ +1 & 0 \\ 0 & +1 \\ \end{array} \right] }[/math] | [math]\displaystyle{ 𝒈_{12} }[/math] | {238.612 2786.314] | [6.940 6.940 0.000 0.000] |
Note that many of these constraint matrices ended up identifying the same tuning. Because of relationships between the prime factors of the target-intervals, some of these points in tuning damage space turned out to be the same, or at least vertically aligned, thus giving the same tuning. So, we didn't actually end up with 22 different candidate [math]\displaystyle{ 𝒈_i }[/math] to compare. We only got 12 different candidate tunings. (Also note that one of our constraint matrices failed to find any tuning at all! That's the one that tried to find the place where [math]\displaystyle{ \frac21 }[/math] and [math]\displaystyle{ \frac31 }[/math] are pure simultaneously. Like we said, it cannot be done.)
Let's rework this table a bit. We won't worry about the constraint matrices we used to get here anymore; done with those. We'll just worry about the unique candidate tunings, their damage lists, and we'll add a new column for their maximum values.
| Candidate generator tuning map | Target-interval damage list | Max damage | |
|---|---|---|---|
| [math]\displaystyle{ 𝒈_i }[/math] | {[math]\displaystyle{ g_1 }[/math] [math]\displaystyle{ g_2 }[/math]] | [math]\displaystyle{ \textbf{d} }[/math] | [math]\displaystyle{ \max(\textbf{d}) }[/math] |
| [math]\displaystyle{ 𝒈_1 }[/math] | {238.612 2793.254] | [6.940 6.940 6.940 6.940] | 6.940 |
| [math]\displaystyle{ 𝒈_2 }[/math] | {238.612 2779.374] | [6.940 6.940 6.940 6.940] | 6.940 |
| [math]\displaystyle{ 𝒈_3 }[/math] | {233.985 2816.390] | [30.075 30.075 30.075 30.075] | 30.075 |
| [math]\displaystyle{ 𝒈_4 }[/math] | {233.985 2756.240] | [30.075 30.075 30.075 30.075] | 30.075 |
| [math]\displaystyle{ 𝒈_5 }[/math] | {233.985 2696.090] | [30.075 30.075 90.225 30.075] | 90.225 |
| [math]\displaystyle{ 𝒈_6 }[/math] | {239.215 2790.240] | [3.923 11.769 3.923 3.923] | 11.769 |
| [math]\displaystyle{ 𝒈_7 }[/math] | {238.133 2783.200] | [9.334 3.111 3.111 3.111] | 9.334 |
| [math]\displaystyle{ 𝒈_8 }[/math] | {240.000 2786.314] | [0.000 18.045 0.000 18.045] | 18.045 |
| [math]\displaystyle{ 𝒈_9 }[/math] | {240.000 2804.360] | [0.000 18.045 18.045 0.000] | 18.045 |
| [math]\displaystyle{ 𝒈_{10} }[/math] | {237.744 2786.314] | [11.278 0.000 0.000 11.278] | 11.278 |
| [math]\displaystyle{ 𝒈_{11} }[/math] | {237.744 2775.040] | [11.278 0.000 11.278 0.000] | 11.278 |
| [math]\displaystyle{ 𝒈_{12} }[/math] | {238.612 2786.310] | [6.940 6.940 0.000 0.000] | 6.940 |
From here, we try to pick our minimax tuning. Skimming the last column of this table quickly, we can see that the minimum out of all of our maximum damages is 6.940.
However, alas! We can minimize the maximum damage to this amount using not just one of these candidate generator tuning maps, and not just two of them, but three different ones of them all achieve this feat. Well, we'll just have to tie-break between them, then.
Basic tie-breaking example: Understanding the tie
The tied minimax tunings are [math]\displaystyle{ 𝒈_1 }[/math], [math]\displaystyle{ 𝒈_2 }[/math], and [math]\displaystyle{ 𝒈_{12} }[/math]. These have tuning maps of {238.612 2793.254], {238.612 2779.374], and {238.612 2786.314], respectively. Notice that all three of these give the same tuning for the first generator, [math]\displaystyle{ g_1 }[/math], namely, 238.612 ¢. This tells us that these three tunings can be found on a line together, and it also tells us that this line is perpendicular to the axis for [math]\displaystyle{ g_1 }[/math]. As for the tuning of the second generator [math]\displaystyle{ g_2 }[/math], then, there's going to be one of these three tuning maps which gives the value in-between the other two; that happens here to be the last one, [math]\displaystyle{ 𝒈_{12} }[/math]. Its [math]\displaystyle{ g_2 }[/math] value is 2786.314 ¢, which is in fact exactly halfway between the other two tuning maps' tuning of [math]\displaystyle{ g_2 }[/math], at 2779.374 ¢ and 2793.254 ¢ (you may also notice that 2786.314 is the just tuning of [math]\displaystyle{ \frac51 }[/math]).
Based on what we know from visualizing the tuning damage space earlier, we can suppose that candidate tunings [math]\displaystyle{ 𝒈_1 }[/math] and [math]\displaystyle{ 𝒈_2 }[/math] are the ones that bound the ends of the line segment region of tied minimax tunings. Remember, [math]\displaystyle{ 𝒈_1 }[/math], [math]\displaystyle{ 𝒈_2 }[/math], and [math]\displaystyle{ 𝒈_{12} }[/math] are not really the only tunings tied for minimax tuning; they're merely special/important/representative tunings that are tied for minimax damage. In concrete terms, it's not just {238.612 2793.254], {238.612 2779.374], and {238.612 2786.314] that deal the minimax damage of 6.940. Any tuning with [math]\displaystyle{ g_1 = 238.612 }[/math] and [math]\displaystyle{ 2779.374 \leq g_2 \leq 2793.254 }[/math] deals this same minimax damage, such as {238.612 2794.000] or {238.612 2787.878]. Any of those will satisfy someone looking only to minimax damage. But we're looking for a true optimum in here. We know there will be some other reason to prefer one of these to all the others.
As for [math]\displaystyle{ 𝒈_{12} }[/math], it's halfway between [math]\displaystyle{ 𝒈_1 }[/math] and [math]\displaystyle{ 𝒈_2 }[/math], although from this view we can't specifically see it. That's because this tuning came from one of our constraint matrices we built from a STIC, which is to say that it's for one of our zero-damage points, where target-interval tuning damage graph creases cross each other along the zero-damage floor. So to see this point better, we'll need to rotate our view on tuning damage space, and take a look from below.
We've drawn a dashed white line segment to indicate the basic minimax tied range from the previous diagram, which is along the crease between the blue and yellow graphs. That part of their crease is not directly visible here, because it's exactly the part that's covered up by that green bit in the middle. Then, we've drawn a finely-dotted cyan line straight down from that basic minimax tied range's line segment at a special point: where the green and red creases cross.
Yes, that's right: it turns out that the place the green and red creases cross is directly underneath the line where the blue and yellow creases cross. This is no coincidence. As hinted at earlier, it's because of our choices of target-intervals, and in particular their prime compositions. If you think about it, along the blue/yellow crease, that's where blue [math]\displaystyle{ \frac21 }[/math] and yellow [math]\displaystyle{ \frac31 }[/math] have the same damage. But there's two places where that happens: where they have the same exact error, and where they have the same error but opposite sign (one error is positive and the other negative). This happens to be the latter type of crossing. And also remember that we're using a unity-weight damage here, i.e. where damage is equal to absolute error. So if [math]\displaystyle{ \frac21 }[/math] and [math]\displaystyle{ \frac31 }[/math] have opposite error, that means that their errors cancel out when you combine them to the interval [math]\displaystyle{ \frac61 }[/math]. So [math]\displaystyle{ \frac61 }[/math] is tuned pure along this crease (if this isn't clear, please review The meaning of the ReDPOTIC product). And along the green [math]\displaystyle{ \frac51 }[/math] damage graph's crease along the zero-damage floor, [math]\displaystyle{ \frac51 }[/math] has zero-damage, i.e. is also pure. So wherever that green crease passes under the blue/yellow crease, that means that both [math]\displaystyle{ \frac61 }[/math] and [math]\displaystyle{ \frac51 }[/math] are pure there. So what should we expect to find with the red hyper-V here, the one for [math]\displaystyle{ \frac65 }[/math]? Well, being comprised exactly and only of [math]\displaystyle{ \frac61 }[/math] and [math]\displaystyle{ \frac51 }[/math], which are both tuned pure, it too must be tuned pure. So no surprise that the red graph crease crosses under the blue/yellow crease at the exact same point as the green crease does.
This—as you will probably not be surprised—is our third tied candidate tuning, [math]\displaystyle{ 𝒈_{12} }[/math], the one that also happens to be halfway between [math]\displaystyle{ 𝒈_1 }[/math] and [math]\displaystyle{ 𝒈_2 }[/math]. We identified this as a point of interest on account of the fact that two damage graphs crossed along the zero-damage floor, thus giving us enough damage graphs to specify a point. This isn't just any point along the floor: again, this point came from one of our STICs.
Intuitively, we should know at this time that this is our true optimum tuning, and we have labeled it as such in the diagram. Any other point along the white dashed line, in our basic minimax region, will minimax damage to [math]\displaystyle{ \frac21 }[/math] and [math]\displaystyle{ \frac31 }[/math] at 6.940 ¢(U). But if we go anywhere along this line segment region other than this one special point, the damage to our other two target-intervals, [math]\displaystyle{ \frac51 }[/math] and [math]\displaystyle{ \frac65 }[/math], will be greater than 0. Sure, the main thing we've been tasked with is to minimize the maximum damage. But while we're at it, why not go the extra mile and tune both [math]\displaystyle{ \frac51 }[/math] and [math]\displaystyle{ \frac65 }[/math] as accurately as we can, too? We might as well. And so [math]\displaystyle{ 𝒈_{12} }[/math] is the best we can do, in a "nested minimax" sense; that's our true optimum tuning. In other words, the maximum damages in each position of these descending-sorted lists are minimized, at least as far down the descending-sorted damage list as they've been abbreviated. (We noted earlier that these zero-damage points might seem unlikely to play much use in identifying a minimax tuning. Well, here's a perfect example of where one saved the day!)
But the question remains: how will the computer be best able to tell that this is the tuning to choose? For that, we're going to need ADSLODs.
Basic tie-breaking: ADSLODs
Before we go any further, we should get straight about what an ADSLOD is. Firstly, like ReDPOTIC, it's another brutally long initialism for a brutally tough-to-name concept of the coinciding-damage method! Secondly, though, it's short for "abbreviated descending-sorted list of damage". Let's work up to that initialism bit by bit.
The "LOD" part is easy: these are "lists of damage". Specifically, they're our target-interval damage lists, which we notate as [math]\displaystyle{ \mathbf{d} }[/math]. (The reversal of "damage list" to "list of damage" is just to make acronym pronounceable. Sorry.)
Next, they are "DS", for "descending-sorted". Note that sorting them causes us to lose track of which target-interval that each damage in the list corresponds to. But that's okay; that information is not important for narrowing down to our optimum tuning. All of our target-intervals are treated equally at this level of the method (we may have weighted their absolute errors differently to obtain our damage, but that's not important here).
This descending-sorting can be thought of as a logical continuation of how we compared the max damage at each tuning. The first entry of each ADSLOD is the max damage done to any interval by that tuning. The second entry of each ADSLOD is the second-most max damage done to any interval by that tuning (which may be the same as the max damage, or less). The third entry is the third-most max damage. And so on.
So in basic tie-breaking, we can compare all the second entries of these ADSLODs to decide which tuning to prefer. And if that doesn't work, then we'll compare all the third entries. And so on. Until we run out of ADSLOD.
Which brings us finally, to the "A" part of the initialism, for "abbreviated". Specifically, we only keep the first [math]\displaystyle{ r + 1 }[/math] entries in these lists, and throw out the rest. This is where [math]\displaystyle{ r }[/math] is the rank of the temperament, or in other words, the count of generators it has. Again, this is the dimension of the tuning damage space we're searching, and so its the number of points required to specify a point within it.
Critically, for our purposes here, that means it's the maximum number of target-intervals we could possibly have minimaxed damage to at this time; if we look any further down this list, then we can't guarantee that the damage values we're looking at are as low as they could possibly be at their position in the list. That's why if these ADSLODs are identical all the way down, we need to try something else, in a new tuning damage space.
We'll learn about this in the advanced tie-breaking section later on.
Basic tie-breaking example: Using the ADSLODs
So, the maximum damages in each damage list weren't enough to choose a tuning. Three of them were tied. We're going to need to look at these three tunings in more detail. Here's our table again, reworked some more, so we only compare the three tied minimax tunings [math]\displaystyle{ 𝒈_1 }[/math], [math]\displaystyle{ 𝒈_2 }[/math], and [math]\displaystyle{ 𝒈_{12} }[/math], and so we show their whole ADSLODs now:
| candidate generator tuning map | target-interval damage list | ADSLOD | |
|---|---|---|---|
| [math]\displaystyle{ 𝒈_i }[/math] | {[math]\displaystyle{ g_1 }[/math] [math]\displaystyle{ g_2 }[/math]] | [math]\displaystyle{ \textbf{d} }[/math] | [math]\displaystyle{ \text{sort}_\text{dsc}(\textbf{d})_{1 \ldots r+1} }[/math] |
| [math]\displaystyle{ 𝒈_1 }[/math] | {238.612 2793.250] | [6.940 6.940 6.940 6.940] | [6.940 6.940 6.940] |
| [math]\displaystyle{ 𝒈_2 }[/math] | {238.612 2779.370] | [6.940 6.940 6.940 6.940] | [6.940 6.940 6.940] |
| [math]\displaystyle{ 𝒈_{12} }[/math] | {238.612 2786.310] | [6.940 6.940 0.000 0.000] | [6.940 6.940 0.000] |
By comparing maximum damages between these candidate tunings already, we've already checked the first entries in each of these ADSLODs. So we start with the second column. Can we tie-break here? No. We've got 6.940, 6.940, and 6.940, respectively. Another three-way tie. Though we've noted that in ADSLODs it no longer matters to which target-intervals the damages are dealt, it may still be understanding-confirming to think through which target-intervals these damages must be for. Well, these first two 6.940 entries must be for the coinciding blue and yellow damages above, the blue/yellow crease, that is, where the damage to [math]\displaystyle{ \frac21 }[/math] and [math]\displaystyle{ \frac31 }[/math] is the same.
Well, one last chance to tie-break before we may need to fall back to advanced tie-breaking. Can we break the tie using the third entries of these ADSLODs. Why, yes! We can! Phew. While [math]\displaystyle{ 𝒈_1 }[/math] and [math]\displaystyle{ 𝒈_2 }[/math] are still tied for 6.940 even in the third position, with [math]\displaystyle{ 𝒈_{12} }[/math] we get by with only 0.000. This could be thought of as corresponding either to the damage for [math]\displaystyle{ \frac51 }[/math] or for [math]\displaystyle{ \frac65 }[/math]. It doesn't matter which one. At [math]\displaystyle{ 𝒈_1 }[/math] and [math]\displaystyle{ 𝒈_2 }[/math] the damage one or both of these two other target-intervals has gotten so big that it's just surpassing that of the damage to [math]\displaystyle{ \frac21 }[/math] and [math]\displaystyle{ \frac31 }[/math] (that's why these are the bounds of the minimax range). But at [math]\displaystyle{ 𝒈_{12} }[/math] one or both of them (we know it's both) is zero.
Note that we refer to equal damages between tunings as "ties"; this is where we have a contest. We are careful to refer to equal damages within tunings as "coinciding", which does double-duty at representing equality and intersection of the target-interval damage graphs.
Thus concludes our demonstration of basic tie-breaking.
When basic tie-breaking fails
We can modify just one thing about our example here in order to cause the basic tie-breaking to fail. Can you guess what it is? We'll give you a moment to pause the article and think. (Haha.)
The answer is: we would only need to remove [math]\displaystyle{ \frac65 }[/math] from our target-interval set. Thus, we'd be finding the {2/1, 3/1, 5/1} minimax-U tuning of blackwood, or more succinctly, its primes minimax-U tuning.[note 1]
And why does basic tie-breaking fail without [math]\displaystyle{ \frac65 }[/math] in the set? This question is perhaps best answered by looking at the from-below view on tuning damage space. But for completeness, let's take a gander at the from-above view first, too.
This looks pretty much the same as before. It's just that there's no red hyper-V damage graph for [math]\displaystyle{ \frac65 }[/math] slicing through here anymore. But it's actually the same exact tied minimax range still.
So what can we see from below, then?
Ah, so there's the same dashed white line as before for the same basic minimax tied range on the other side. But do you notice what's different? Without the red hyper-V graph for [math]\displaystyle{ \frac65 }[/math] anymore, we have no crease coming through to cross the green crease at that magic point we care about! So, this point and its [6.940 6.940 0.000] ADSLOD never comes up for consideration. And so when we get to the step where we compare ADSLODs, we'd only have the two that completely tie with [6.940 6.940 6.940], and get stuck. We would have needed a second crease along the floor to intersect, to cause the method to identify it as a point of interest.
Remember, this coinciding-damage method is only designed to gather points, specifically by checking all the possible places where enough of these damage graphs intersect at once in order to define a point. In 3D tuning damage space we need two creases to intersect at once along the floor to define a point. But here on the floor we've just got one graph, with its crease's line going one direction, and up above we've got two graphs meeting at a different crease, their line going the perpendicular direction. There is a tuning, i.e. a vertical line through tuning damage space, which both of these lines pass through, and that's the tuning we want, the true optimum tuning. But because these lines don't actually cross at a point in tuning damage space, one just passes right over the other, the method misses it.
While we as human observers can pick out right away that what we want is the tuning at that vertical line where the one crease passes over the other, in terms of design of the computer algorithm this method uses, it turns out to be more efficient overall to design it in a way such that it reaches that conclusion another way. And that's what we're going to look at next; that's the advanced tie-breaking.
As a sneak preview, the way the computer will do it is to take the cross-section of tuning damage space wherever that tied range occurs, and gather a new set of coinciding-damage points. From its cross-section view, a 2D tuning damage graph, the blue and yellow graphs will look like horizontal lines, and the green graph will look like a V underneath it. From that view, it will be obvious that the tip of this green V, where the damage to [math]\displaystyle{ \frac51 }[/math] is zero, will be the true optimum tuning. Something like this:
Advanced tie-breaking: Intro
So: comparing ADSLODs (abbreviated descending-sorted list of damages) is not always sufficient to identify a unique optimum tuning. Basic tie-breaking is insufficient whenever more than one coinciding-damage point has the same exact ADSLOD, and these identical ADSLODs also happen to be the best ones insofar as they give a nested minimax tuning, where by "nested minimax" we mean that the maximum damages in each position of these descending-sorted list are minimized, at least as far down the descending-sorted damage list as they've been abbreviated. We might call these "TiNMADSLODs": tied nested minimax abbreviated descending-sorted lists of damage.
In such situations, what we need to do is gather a new set of points, for a new coinciding-damage point set. But it's not like we're starting over from scratch here; it just means that we need to perform another iteration of the same process, but this time searching for tunings in a more focused region of our tuning damage space. In other words, we didn't waste our time or effort; we did make progress with our first coinciding-damage point set. The tunings with TiNMADSLODs which we identified in the previous iteration are valuable: they're what we need to proceed. What these tell us, essentially, is that the true optimum tuning must be somewhere in between them. Our tie indicates not simply two, three, or any finite number of tied tunings; it indicates a continuous range of tunings which all satisfy this tie. The trick now is to define what range, or region, that we mean exactly by "in between", and describe it mathematically. But even more importantly, we've got to figure out how to narrow down the smaller (but still infinite!) set of tuning points within this region to a new set of candidate true optimum tunings.
A slightly oversimplified way to describe this type of tie-breaking would be: whenever we find a range of tunings tied for minimax damage, in order to tie-break within this range, we temporarily ignore the target-intervals in the set which received the actual minimaxed damage amount, then search this range for the tuning that minimizes the otherwise maximum damage. And we just keep repeating this process until we finally identify a single true optimum tuning.
Advanced tie-breaking example: Setup
To help illustrate advanced tie-breaking, we're going to look at a minimax-C tuning of augmented temperament, with mapping [⟨3 0 7] ⟨0 1 0]}. In particular, we're going to use the somewhat arbitrary target-interval set [math]\displaystyle{ \{ \frac32, \frac52, \frac53, \frac83, \frac95, \frac{16}{5}, \frac{15}{8}, \frac{18}{5} \} }[/math]. As a rank-2 temperament, we're going to be searching 3D tuning damage space. This temperament divides the octave into three parts, so our ballpark [math]\displaystyle{ g_1 }[/math] is 400 ¢, and our second generator [math]\displaystyle{ g_2 }[/math] is a free generator for prime 3, so it's going to be ballpark its pure tuning of 1901.955 ¢.
Here's an aerial view on the tuning damage space, where we've "clipped" every damage graph hyper-V where it has gone out-of-bounds, above 150 ¢(C) damage; that is, we've colored it in grey and flattened it across the top of the box of our visualization. This lets us focus in clearly on the region of real interest, which is where all target-intervals' damages are less than this cap at the same time. This is the multicolored crater in the middle here:
Like the blackwood example we looked at in the basic tie-breaking section, this max damage graph also ends up with a line segment region on top that's exactly parallel to the floor, therefore every tuning along this range is tied for minimax. This happens to be 92.557 ¢(C) damage.
(Again, this was made possible by the prime compositions of our target-intervals, and how this temperament maps them. Specifically, the yellow graph here is [math]\displaystyle{ \frac{15}{8} }[/math] and the blue graph is [math]\displaystyle{ \frac{18}{5} }[/math]. The first interval [math]\displaystyle{ \frac{18}{5} }[/math] is one augmented comma [math]\displaystyle{ \frac{128}{125} }[/math] off from [math]\displaystyle{ (\frac{15}{8})^2 = \frac{225}{64} }[/math]. In vector form, we have [1 2 -1⟩ = 2×[-3 1 1⟩ - [-7 0 3⟩). So since [math]\displaystyle{ \frac{15}{8} }[/math] maps to [-2 1}, that means that [math]\displaystyle{ \frac{18}{5} }[/math] maps to [-4 2}. And since [-4 2} = 2×[-2 1}, i.e. a simple scalar relates these two mapped intervals. In other words, they have the same proportion of generators, this means their damage graph creases will be parallel.)
Every tuning along this range is tied for second-most minimax, still 92.557 ¢(C), on account of it being a crease between two target-interval damage graphs. But we can look 3 positions down the ADSLODs here on account of it being a rank-2 ([math]\displaystyle{ r = 2 }[/math]) temperament and so tuning damage space is [math]\displaystyle{ (r + 1 = 3) }[/math]-dimensional. But this doesn't help us. Because the reason this line segment range ends at the endpoints it ends at is because that's exactly where some third damage graph finally shoots higher above 92.557 ¢(C). So the third entries in the these ADSLODs are also tied for 92.557 ¢(C).
And here's the key issue: this augmented example is more like the variation we briefly looked at for blackwood, where basic tie-breaking failed us, on account of there not happening to be any other identifiable points of interest immediately below the minimax line segment. Below this line segment, then, there must be only sloping planes, or possibly some other creases, but no points. So in order to nested minimax further, we'll have to create some points below it, by taking a cross-section right along that tied range, and seeing which damage graph V's we can find to intersect in that newly focused 2D tuning damage space.
The two tied minimax tunings we've identified are {399.809 1901.289] and {400.865 1903.401]. So, we're already very close to an answer: our [math]\displaystyle{ g_1 }[/math] has been narrowed down almost to the range of a single cent, while our [math]\displaystyle{ g_2 }[/math] is narrowed down almost to the range of two cents.
And here's what that cross-section looks like.
(Sorry for the changed colors of the graphs. Not enough time to nudge Wolfram Language into being nice.)
For now, don't worry too much about the horizontal axis, but know that the range from 0.0 to 1.0 is our actual tied minimax range. It's a little tricky to label this axis. It's not as simple as it was when we showed the cross-section for blackwood, because that cross-section was parallel to one of the generator axes, so we could simply label it with that generator's sizes. But this cross-section is diagonal through our tuning damage space (from above), so no such luck! (Well, we chose an example like this by design, to show how the method grapples with tricky situations like this. We'll get to it soon enough.)
We can see that 0.0 and 1.0 are the points where another damage graph crosses above the horizontal line otherwise on top, the horizontal line which corresponds to the crease between the damage graphs for [math]\displaystyle{ \frac{15}{8} }[/math] and [math]\displaystyle{ \frac{18}{5} }[/math], which appears as brown here. So those two target-intervals which cause the range to end at either end are [math]\displaystyle{ \frac{16}{5} }[/math] and [math]\displaystyle{ \frac95 }[/math].
These two target-intervals [math]\displaystyle{ \frac{16}{5} }[/math] and [math]\displaystyle{ \frac95 }[/math] also happen to be the two target-intervals whose crossing is going to yield us the point we need to identify the true optimum tuning! We can see that if we pretend that the horizontal [math]\displaystyle{ \frac{15}{8} }[/math] and [math]\displaystyle{ \frac{18}{5} }[/math] aren't here, and then just gather all the points in this view where target-intervals cross or bounce off the zero-damage floor as normal (that's our second iteration of the method, gathering another round of coinciding-damage points!), and check the maximum damages at each of those tunings, and then choose the tuning with the lowest maximum damage (possibly involving basic tie-breaking with ADSLODs, though we won't need it in this particular case), we're going to find that point where [math]\displaystyle{ \frac{16}{5} }[/math] and [math]\displaystyle{ \frac95 }[/math] cross. To be clear, those are the yellow and blue graphs here, and they cross about a third of the way from 0.0 to 1.0.
Spoiler alert: the tuning this crossing identifies is {400.171 1902.011], which as it should be is somewhere between our previously tied tunings of {399.809 1901.289] and {400.865 1903.401]. This is indeed our true optimum tuning. But in order to understand how we would determine these exact cents values from this cross-sectioning process, we're going to have to take a little detour. In order to understand how these further iterations of the coinciding-damage method work, we need to understand the concept of blends.
Blends: Abstract concept
Let's begin to learn blends in the abstract (though you may well try to guess ahead as to the application of these concepts to the RTT problem at hand).
Suppose we want a good way to describe a line segment between two other points [math]\displaystyle{ \mathbf{A} }[/math] and [math]\displaystyle{ \mathbf{B} }[/math], or in other words, any point between these two points. We could describe this arbitrary point [math]\displaystyle{ \mathbf{P} }[/math] as some blend of point [math]\displaystyle{ \mathbf{A} }[/math] and point [math]\displaystyle{ \mathbf{B} }[/math], like this: [math]\displaystyle{ \mathbf{P} = x\mathbf{A} + y\mathbf{B} }[/math], where [math]\displaystyle{ x }[/math] and [math]\displaystyle{ y }[/math] are our blending variables.
For example, if [math]\displaystyle{ x=1 }[/math] and [math]\displaystyle{ y=0 }[/math], then [math]\displaystyle{ \mathbf{P} = (1)\mathbf{A} + (0)\mathbf{B} = \mathbf{A} }[/math]. And if [math]\displaystyle{ x=0 }[/math] and [math]\displaystyle{ y=1 }[/math], then similarly [math]\displaystyle{ \mathbf{P} = \mathbf{B} }[/math]. Now if both [math]\displaystyle{ x=0 }[/math] and [math]\displaystyle{ y=0 }[/math], we might say [math]\displaystyle{ \mathbf{P} = \mathbf{O} }[/math], where this point [math]\displaystyle{ \mathbf{O} }[/math] is our origin, out there in space somewhere; however, if we want to use this technique in a useful way, we don't actually need to worry about this origin, because in turns out that if we simply require that [math]\displaystyle{ x+y=1 }[/math], we can ensure that we can only reach points along that line segment connecting [math]\displaystyle{ \mathbf{A} }[/math] to [math]\displaystyle{ \mathbf{B} }[/math]! For example, with [math]\displaystyle{ x = \frac12 }[/math] and [math]\displaystyle{ y = \frac12 }[/math], we describe the point exactly halfway between [math]\displaystyle{ \mathbf{A} }[/math] and [math]\displaystyle{ \mathbf{B} }[/math].
We can generalize this idea to higher dimensions. That was the 1-dimensional case. Suppose instead we have three points: [math]\displaystyle{ \mathbf{A} }[/math], [math]\displaystyle{ \mathbf{B} }[/math], and [math]\displaystyle{ \mathbf{C} }[/math]. Now the region bounded by these points is no longer a 1D line segment. It's a 2D plane segment. Specifically, it's a triangle, with points at our three points. And now our point [math]\displaystyle{ \mathbf{P} }[/math] that is somewhere inside this triangle can be described as [math]\displaystyle{ \mathbf{P} = x\mathbf{A} + y\mathbf{B} + z\mathbf{C} }[/math], where now [math]\displaystyle{ x + y + z = 1 }[/math].
And so on to higher and higher dimensions.
One fewer blending variable; anchor
However, let's pause at the 2-dimensional case to make an important observation: we don't actually need one blending variable for each point being blended. In practice, since our blending variables are required to sum to 1, we only really need one fewer blending variable than we have points. When [math]\displaystyle{ x + y + z = 1 }[/math], then we know [math]\displaystyle{ x }[/math] must equal [math]\displaystyle{ 1 - y - z }[/math], so that:
[math]\displaystyle{
\begin{aligned}
x + y + z &= 1
(1 - y - z) + y + z &= 1
1 - \cancel{y} - \cancel{z} + \cancel{y} + \cancel{z} &= 1
1 &= 1
\end{aligned}
}[/math]
But how would we modify our [math]\displaystyle{ \mathbf{P} = x\mathbf{A} + y\mathbf{B} + z\mathbf{C} }[/math] formula to account for this unnecessity of [math]\displaystyle{ x }[/math]?
We note that the choice of [math]\displaystyle{ x }[/math]—and therefore [math]\displaystyle{ \mathbf{A} }[/math]—was arbitrary; we simply felt that picking the first blending variable and point would be simplest, and provide the convenience of consistency when examples from different dimensions were compared.
Clearly we can't simply drop [math]\displaystyle{ x }[/math] with no further modifications; in that case, we'd have [math]\displaystyle{ \mathbf{P} = \mathbf{A} + y\mathbf{B} + z\mathbf{C} }[/math], so now every single point is going to essentially have [math]\displaystyle{ x = 1 }[/math], or in other words, a whole [math]\displaystyle{ \mathbf{A} }[/math] mixed in.
Well, the key is to change what [math]\displaystyle{ y }[/math] and [math]\displaystyle{ z }[/math] blend in. Think of it this way: if we always have a full [math]\displaystyle{ \mathbf{A} }[/math] mixed in to our [math]\displaystyle{ \mathbf{P} }[/math], then all we need to worry about are the deviations from this [math]\displaystyle{ \mathbf{A} }[/math]. That is, we need the formula like so:
[math]\displaystyle{
\mathbf{P} = \mathbf{A} + y(\mathbf{B} - \mathbf{A}) + z(\mathbf{C} - \mathbf{A})
}[/math]
So now when [math]\displaystyle{ y=1 }[/math] and [math]\displaystyle{ z=0 }[/math], we still find [math]\displaystyle{ \mathbf{P} = \mathbf{B} }[/math] as before:
[math]\displaystyle{
\mathbf{P} = \mathbf{A} + y(\mathbf{B} - \mathbf{A}) + z(\mathbf{C} - \mathbf{A})
\mathbf{P} = \mathbf{A} + (1)(\mathbf{B} - \mathbf{A}) + (0)(\mathbf{C} - \mathbf{A})
\mathbf{P} = \mathbf{A} + \mathbf{B} - \mathbf{A}
\mathbf{P} = \mathbf{B}
}[/math]
And similarly [math]\displaystyle{ \mathbf{P} = \mathbf{C} }[/math] for [math]\displaystyle{ y = 0 }[/math] and [math]\displaystyle{ z = 1 }[/math].
For convenience, we could refer to this arbitrary point [math]\displaystyle{ \mathbf{A} }[/math] our anchor'.
For a demonstration of the relationship between the formula with [math]\displaystyle{ \mathbf{A} }[/math] extracted and the original formula, please see #Derivation of extracted anchor.
FYI, the general principle at work with blends here is technically called a "convex combination"; feel free to read more about them now if you're not comfortable with the idea yet.
Combining insights
Okay, and we're almost ready to tie this back to our RTT application. Realize that back in 1D, we'd have
[math]\displaystyle{
\mathbf{P} = \mathbf{A} + y(\mathbf{B} - \mathbf{A})
}[/math]
So now think back to the 2D damage graph for the second coinciding-damage point set we looked at in the augmented temperament example of the previous section. Every tuning damage graph we've ever looked at has had damage as the vertical axis, and every other axis corresponding to the tuning of a generator… except that graph! Recall that we took a cross-section of the original 3D tuning damage space. So the horizontal axis of that graph is not any one of our generators. As we mentioned, it runs diagonally across the zero-damage floor, since the sizes of the two target-intervals which define it depend on the sizes of both these generators.
What is the axis, then, though? Well, one way to put it would be, recalling that we took it as the cross-section between two TiNMADSLOD tunings, would be this: it's a blending variable. It's just like our abstract case, but here, the points we're blending between are tunings. In one direction we increase the blend of the first of these two tunings, and in the other direction we increase the blend of the other tuning. Or, in recognition of the "one fewer" insight, we have one blending variable that controls from 0, not at all, to 1, completely, by how much we blend away from our anchor tuning to the other tuning. We could imagine this is just like our point [math]\displaystyle{ \mathbf{P} }[/math] which is a blend between [math]\displaystyle{ \mathbf{A} }[/math], [math]\displaystyle{ \mathbf{B} }[/math] using blend variables [math]\displaystyle{ x }[/math] and [math]\displaystyle{ y }[/math]:
But next, we've got to grow up, and stop using these vague, completely abstract points. We've got to commit to fully applying this blending concept to RTT. Let's start using tuning-related objects now.
Substituting RTT objects in
So we're looking along this line segment for our true optimum tuning, our equivalent of point [math]\displaystyle{ \mathbf{P} }[/math]. Let's call it [math]\displaystyle{ 𝒈 }[/math], simply our generator tuning map.
But our points [math]\displaystyle{ \mathbf{A} }[/math] and [math]\displaystyle{ \mathbf{B} }[/math] are also generator tuning maps. Let's call those [math]\displaystyle{ 𝒈_0 }[/math] and [math]\displaystyle{ 𝒈_1 }[/math], that is, with subscripted indices.
We've zero-indexed these because [math]\displaystyle{ 𝒈_0 }[/math] will be our anchor tuning, and therefore will be to large extent special and outside the normal situation; in particular, it won't need a corresponding blend variable, and it's better if our blend variables can be one-indexed like how we normally index things.
Right, so while the A, B, C and x, y, z thing worked fine for the abstract case, it'll be better moving forward if we use the same variable letter for the same type of thing, and use subscripted indices to distinguish them, so let's replace the blending variable that corresponded to point [math]\displaystyle{ \mathbf{B} }[/math], which was [math]\displaystyle{ y }[/math], with [math]\displaystyle{ b_1 }[/math], since that corresponds with [math]\displaystyle{ 𝒈_1 }[/math], which is what [math]\displaystyle{ \mathbf{B} }[/math] became.
So here's what we've got so far:
[math]\displaystyle{
𝒈 = 𝒈_{0} + b_1(𝒈_{1} - 𝒈_{0})
}[/math]
Generalizing to higher dimensions: The blend map
Now that's the formula for finding a generator tuning map [math]\displaystyle{ 𝒈 }[/math] somewhere in the line segment between two other tied generator tuning maps [math]\displaystyle{ 𝒈_0 }[/math] and [math]\displaystyle{ 𝒈_1 }[/math]. And that would work fine for our augmented temperament example. But before crawl back into the weeds on that example, let's solidify our understanding of the concepts by generalizing them, so we can feel confident we could use them for any advanced tie-breaking situation.
It shouldn't be hard to see that for a 2D triangular case—if we wanted to find a [math]\displaystyle{ 𝒈 }[/math] somewhere in between tied [math]\displaystyle{ 𝒈_0 }[/math], [math]\displaystyle{ 𝒈_1 }[/math], and [math]\displaystyle{ 𝒈_2 }[/math]—we'd use the formula:
[math]\displaystyle{
𝒈 = 𝒈_{0} + b_1(𝒈_{1} - 𝒈_{0}) + b_2(𝒈_{2} - 𝒈_{0})
}[/math]
Each new tied [math]\displaystyle{ 𝒈_i }[/math] adds a new blending variable, which scales its delta with the anchor tuning [math]\displaystyle{ 𝒈_0 }[/math].
We should recognize now that we might have an arbitrarily large number of tied tunings. This is a perfect job for a vector, that is, we should gather up all our [math]\displaystyle{ b_i }[/math] into one object, a vector called [math]\displaystyle{ 𝒃 }[/math].
Well, it is a vector, but in particular its a row vector, or covector, which we more commonly refer to as a map. This shouldn't be terribly surprising that it's a map, because we said a moment ago that while our tuning damage graph's axes (other than the damage axis) usually correspond to generator (tuning map)s, for our second iteration of this method here, in the cross-section view, those axes correspond to blending variables. So in general, the blend map [math]\displaystyle{ 𝒃 }[/math] takes the place of the generator tuning map [math]\displaystyle{ 𝒈 }[/math] in iterations of the coinciding-damage method beyond the first.
The deltas matrix
Here's the full setup, for an arbitrary count of ties:
[math]\displaystyle{
𝒈 =
\begin{array} {c}
\text{anchor tuning} \\
𝒈_{0} \\
\end{array}
+
\begin{array} {c}
\text{blend map} \; 𝒃 \\
\left[ \begin{array} {c}
b_1 & b_2 & \cdots & b_{τ-1}
\end{array} \right] \\
\end{array}
\begin{array} {c}
\text{tuning deltas} \\
\left[ \begin{array} {c}
𝒈_{1} - 𝒈_{0} \\
𝒈_{2} - 𝒈_{0} \\
\vdots \\
𝒈_{τ-1} - 𝒈_{0} \\
\end{array} \right] \\
\end{array}
}[/math]
We're using the variable [math]\displaystyle{ τ }[/math] here for our count of tied tunings. (It's the Greek letter "tau". We're avoiding 't' because "tuning" and "temperament" both already use that letter.)
We'd like a simpler way to refer to the big matrix on the right. As we've noted above, it's a matrix of deltas. In particular, it's a matrix of deltas between the anchor tied tuning and the other tied tunings.
We don't typically see differences between generator tuning maps in RTT. These map differences are cousins to our retuning maps [math]\displaystyle{ 𝒓 }[/math], we suppose, insofar as they're the difference between two tuning maps of some kind, but the comparison ends there, because:
- in the case of a retuning map, one of the maps is just and the other tempered, while in this case both are tempered, and
- in the case of a retuning map, both are prime tuning maps, while in this case both are generator tuning maps.
We can make use of the Greek letter delta and its association with differences. So let's use [math]\displaystyle{ 𝜹_i }[/math] as a substitute for [math]\displaystyle{ 𝒈_i - 𝒈_{0} }[/math]. We may call it a delta of generator tuning maps. The delta [math]\displaystyle{ 𝜹_i }[/math] takes the index [math]\displaystyle{ i }[/math] of whichever tied tuning is the one the anchor tuning is subtracted from. (More payoff for our zero-indexing of those; our deltas here, like our blend map entries, will therefore be one-indexed as per normal.)
Substituting back into our formula, then, we find:
[math]\displaystyle{
𝒈 =
\begin{array} {c}
\text{anchor tuning} \\
𝒈_{0} \\
\end{array}
+
\begin{array} {c}
\text{blend map} \; 𝒃 \\
\left[ \begin{array} {c}
b_1 & b_2 & \cdots & b_{τ-1}
\end{array} \right] \\
\end{array}
\begin{array} {c}
\text{tuning deltas} \\
\left[ \begin{array} {c}
𝜹_1 \\
𝜹_2 \\
\vdots \\
𝜹_{τ-1} \\
\end{array} \right] \\
\end{array}
}[/math]
But we can do a bit better. Let's find a variable that could refer to this whole matrix, whose rows are each generator tuning map deltas. The natural thing seems to be to use the capital version of the Greek letter delta, which is [math]\displaystyle{ \textit{Δ} }[/math]. However, this letter is so strongly associated with use as an operator, for representing the difference in values of the thing just to its right, that probably this isn't the best idea. How about instead we just use the Latin letter [math]\displaystyle{ D }[/math], for "delta". This is our (generator tuning map) deltas matrix.
This lets us simplify the formula down to this:
[math]\displaystyle{
𝒈 = 𝒈_{0} + 𝒃D
}[/math]
How to identify tunings
This formula assumes we already have all of our tied tunings [math]\displaystyle{ 𝒈_0 }[/math] through [math]\displaystyle{ 𝒈_{τ - 1} }[/math] from the previous coinciding-damage point set, i.e. the previous iteration of the algorithm. And so we already know the [math]\displaystyle{ 𝒈_0 }[/math] and [math]\displaystyle{ D }[/math] parts of this equation. This equation, then, gives us a way to find a tuning [math]\displaystyle{ 𝒈 }[/math] given some blend map [math]\displaystyle{ 𝒃 }[/math]. But what we really want to do is identify not just any tunings that are such blends, but particular tunings that are such blends: we want to find the ones that are part of the next iteration's coinciding-damage point set, the ones where damage graphs intersect in our cross-sectional diagram.
We can accomplish this by solving for each [math]\displaystyle{ 𝒃 }[/math] with respect to a given constraint matrix [math]\displaystyle{ K }[/math]. This is just as we solved for each [math]\displaystyle{ 𝒈 }[/math] in the first iteration with respect to each [math]\displaystyle{ K }[/math]; again, [math]\displaystyle{ 𝒃 }[/math] is filling the role of [math]\displaystyle{ 𝒈 }[/math] here now.
So we've got our [math]\displaystyle{ 𝒕\mathrm{T}WK = 𝒋\mathrm{T}WK }[/math] setup. Remember that in the first iteration, [math]\displaystyle{ K }[/math] had [math]\displaystyle{ r }[/math] columns, one for each generator to solve for, since each column corresponds to an unchanged-interval of the tuning. In other words, one column of [math]\displaystyle{ K }[/math] for each entry of [math]\displaystyle{ 𝒈 }[/math]. Well, so we're still going to use constraint matrices to identify tunings here, but now they're going to have [math]\displaystyle{ τ - 1 }[/math] columns, one for each entry in the blend map, which has one entry for each delta of a tied tuning past the anchor tied tuning (a delta with the anchor tied tuning). We can still use the symbol [math]\displaystyle{ K }[/math] for these constraint matrices, even though it's a somewhat different sort of constraint, with a different shape [math]\displaystyle{ (k, τ -1) }[/math].
Next, let's just unpack [math]\displaystyle{ 𝒕 }[/math] to [math]\displaystyle{ 𝒈M }[/math]:
[math]\displaystyle{
𝒈M\mathrm{T}WK = 𝒋\mathrm{T}WK
}[/math]
And substitute [math]\displaystyle{ 𝒈_{0} + 𝒃D }[/math] in for [math]\displaystyle{ 𝒈 }[/math]:
[math]\displaystyle{
(𝒈_{0} + 𝒃D)M\mathrm{T}WK = 𝒋\mathrm{T}WK
}[/math]
Distribute:
[math]\displaystyle{
𝒈_{0}M\mathrm{T}WK + 𝒃DM\mathrm{T}WK = 𝒋\mathrm{T}WK
}[/math]
Work toward isolating [math]\displaystyle{ 𝒃 }[/math].
[math]\displaystyle{
𝒃DM\mathrm{T}WK = 𝒋\mathrm{T}WK - 𝒈_{0}M\mathrm{T}WK
}[/math]
Group on the right-hand side:
[math]\displaystyle{
𝒃DM\mathrm{T}WK = (𝒋 - 𝒈_{0}M)\mathrm{T}WK
}[/math]
Replace [math]\displaystyle{ 𝒈_{0}M }[/math] with [math]\displaystyle{ 𝒕_0 }[/math] which is the corresponding (prime) tuning map to [math]\displaystyle{ 𝒈_{0} }[/math].
[math]\displaystyle{
𝒃DM\mathrm{T}WK = (𝒋 - 𝒕_0)\mathrm{T}WK
}[/math]
We normally see the just tuning map subtracted from the tempered tuning map, not the other way around as we have here. So let's just negate everything. This is no big deal, since [math]\displaystyle{ 𝒃 }[/math] is an unknown variable after all, so we can essentially think of this [math]\displaystyle{ 𝒃 }[/math] as a new [math]\displaystyle{ 𝒃 }[/math] equal to the negation of our old [math]\displaystyle{ 𝒃 }[/math].
[math]\displaystyle{
𝒃DM\mathrm{T}WK = (𝒕_0 - 𝒋)\mathrm{T}WK
}[/math]
So that's just a (prime) retuning map on the right:
[math]\displaystyle{
𝒃DM\mathrm{T}WK = 𝒓_{0}\mathrm{T}WK
}[/math]
We've now reached the point where Keenan's original version of this algorithm would solve directly for [math]\displaystyle{ 𝒃 }[/math], analogous to how it solves directly (and approximately) for [math]\displaystyle{ 𝒈 }[/math] in the first iteration. But when we use the matrix inverse technique—where instead of solving directly (and approximately) for a generator tuning map [math]\displaystyle{ 𝒈 }[/math] we instead solve exactly for a generator embedding [math]\displaystyle{ G }[/math] and can then later obtain [math]\displaystyle{ 𝒈 }[/math] as [math]\displaystyle{ 𝒈 = 𝒋G }[/math]—then here we must be solving exactly for some matrix which we could call [math]\displaystyle{ B }[/math], following the analogy [math]\displaystyle{ 𝒈 : G :: 𝒃 : B }[/math]. (Not to be confused with the subscripted [math]\displaystyle{ B_s }[/math] that we use for basis matrices; these two matrices will probably never meet, though). This will be a [math]\displaystyle{ (d, τ-1) }[/math]-shaped matrix, which we could call the blend matrix.
And this is why we noted the thing earlier about how constraint matrices are about identifying tunings, not optimizing them. If you come across this setup, and see that somehow, for some reason, [math]\displaystyle{ 𝒓_0 }[/math] has replaced [math]\displaystyle{ 𝒋 }[/math], you might want to try to answer the question: why are we trying to optimize things relative to some arbitrary retuning map, now, instead of JI? The problem with that is: it's the wrong question. It's not so much that [math]\displaystyle{ 𝒓_0 }[/math] is a goal or even a central player in this situation. It just sort of works out this way.
It turns out that while [math]\displaystyle{ 𝒋 }[/math] is what relates [math]\displaystyle{ G }[/math] to [math]\displaystyle{ 𝒈 }[/math], it's [math]\displaystyle{ 𝒓_0 }[/math] which relates this [math]\displaystyle{ B }[/math] to [math]\displaystyle{ 𝒃 }[/math]. This shouldn't be hugely surprising, since [math]\displaystyle{ 𝒓_0 }[/math] is sort of "filling the role" of [math]\displaystyle{ 𝒋 }[/math] there on the right-hand side, insofar as it finds itself in the same position as [math]\displaystyle{ 𝒋 }[/math] did in the simpler case.
So we get:
[math]\displaystyle{
𝒓_{0}BDM\mathrm{T}WK = 𝒓_{0}\mathrm{T}WK
}[/math]
And cancel out the [math]\displaystyle{ 𝒓_0 }[/math] on both sides:
[math]\displaystyle{
\begin{aligned}
\cancel{𝒓_{0}}BDM\mathrm{T}WK &= \cancel{𝒓_{0}}\mathrm{T}WK \\
BDM\mathrm{T}WK &= \mathrm{T}WK
\end{aligned}
}[/math]
Then we do our inverse. This is the exact analog of [math]\displaystyle{ G = \mathrm{T}WK(M\mathrm{T}W)^{-1} }[/math]:
[math]\displaystyle{
B = \mathrm{T}WK(DM\mathrm{T}WK)^{-1}
}[/math]
And once we have that, adapting our earlier formula for [math]\displaystyle{ 𝒈 }[/math] from [math]\displaystyle{ 𝒃 }[/math] to give us [math]\displaystyle{ 𝒈 }[/math] from [math]\displaystyle{ B }[/math] instead:
[math]\displaystyle{
𝒈 = 𝒈_{0} + 𝒓_{0}BD
}[/math]
So all in one formula, substituting our formula for [math]\displaystyle{ B }[/math] into that, we have:
[math]\displaystyle{
𝒈 = 𝒈_{0} + 𝒓_{0}\mathrm{T}WK(DM\mathrm{T}WK)^{-1}D
}[/math]
And that's how you find the generators for a tuning corresponding to a coinciding damage point described by [math]\displaystyle{ K }[/math] at whichever point in a tied minimax tuning range it lays.
Computing damage
We could compute the damage list from any [math]\displaystyle{ 𝒈 }[/math], as normal: [math]\displaystyle{ 𝐝 = |𝐞|W = |𝒓\mathrm{T}|W = |(𝒕 - 𝒋)\mathrm{T}|W = |(𝒈M - 𝒋)\mathrm{T}|W }[/math]. But actually we don't have to recover [math]\displaystyle{ 𝒈 }[/math] from [math]\displaystyle{ B }[/math] in order to compute damage. There's a more expedient way to compute it. If:
[math]\displaystyle{
𝒃DM\mathrm{T}WK = 𝒓_{0}\mathrm{T}WK
}[/math]
then pulling away the constraint, we revert from an equality to an approximation:
[math]\displaystyle{
𝒃DM\mathrm{T}W ≈ 𝒓_{0}TW
}[/math]
And the analogous thing we minimize to make this approximation close (review the basic algebraic setup if need be) would be:
[math]\displaystyle{
|(𝒃DM - 𝒓_{0})\mathrm{T}|W
}[/math]
So the damage caused by a blend map [math]\displaystyle{ 𝒃 }[/math] is:
In other words, we can find it using the same formula as we normally use, [math]\displaystyle{ |(𝒈M - 𝒋)\mathrm{T}|W }[/math], but using [math]\displaystyle{ 𝒃 }[/math] instead of [math]\displaystyle{ 𝒈 }[/math], [math]\displaystyle{ DM }[/math] instead of [math]\displaystyle{ M }[/math], and [math]\displaystyle{ 𝒓_0 }[/math] instead of [math]\displaystyle{ 𝒋 }[/math]. Which is just what we end up with upon substituting [math]\displaystyle{ 𝒈_0 + 𝒃D }[/math] in for [math]\displaystyle{ 𝒈 }[/math]:
[math]\displaystyle{
|((𝒈_{0} + 𝒃D)M - 𝒋)\mathrm{T}|W
|(𝒈_{0}M + 𝒃DM - 𝒋)\mathrm{T}|W
|(𝒕_{0} + 𝒃DM - 𝒋)\mathrm{T}|W
|(𝒃DM - 𝒓_{0})\mathrm{T}|W
}[/math]
Blends of blends
Okay then. So if we find the blend for each point of our next iteration's coinciding-damage point set, and use that to find the damage for that blend, then hopefully this time we find a unique minimax as far down the lists as we can validly compare.
And if not, we rinse and repeat. Which is to say, where here our generators are expressed in terms of a blend of other tunings, after another iteration of continued searching, our generators would be expressed as a blend of other tunings, where each blend was itself a blend of other tunings. And so on.
Apply formula to example
To solidify our understanding, let's finally return to that real-life augmented example, and apply the concepts we learned to it!
At the point our basic tie-breaking failed, we found two tied tunings. Let the first one be our anchor tuning, [math]\displaystyle{ 𝒈_0 }[/math], and the second be our [math]\displaystyle{ 𝒈_1 }[/math]:
[math]\displaystyle{
𝒈_0 = \left[ \begin{array} {r} 399.809 & 1901.289 \end{array} \right] \\
𝒈_1 = \left[ \begin{array} {r} 400.865 & 1903.401 \end{array} \right] \\
}[/math]
Let me drop the cross-section diagram here again, for conveniently close reference, and with some smarter stuff on top of it this time:
So our prediction looks like it should be about [math]\displaystyle{ b_1 = 0.35 }[/math] in order to nail that point we identified earlier where the red and olive lines cross at the triangle underneath the flat tie line across the top.
And here's the formula again for a tuning here from [math]\displaystyle{ K }[/math], again, for conveniently close reference:
[math]\displaystyle{
B = \mathrm{T}WK(DM\mathrm{T}WK)^{-1}
}[/math]
Let's get easy stuff out of the way first. We know [math]\displaystyle{ M }[/math] = [⟨3 0 7] ⟨0 1 0]}. As for [math]\displaystyle{ \mathrm{T} }[/math], in the beginning we gave it as [math]\displaystyle{ \{ \frac32, \frac52, \frac53, \frac83, \frac95, \frac{16}{5}, \frac{15}{8}, \frac{18}{5} \} }[/math]. And [math]\displaystyle{ W }[/math] will just be the diagonal matrix of their log-product complexities, since we went with minimax-C tuning here.
Okay, how about [math]\displaystyle{ K }[/math] next then. Since [math]\displaystyle{ τ = 2 }[/math] here—we have two tied tunings—we know [math]\displaystyle{ K }[/math] will have only [math]\displaystyle{ τ - 1 = 1 }[/math] column. And [math]\displaystyle{ k = 8 }[/math], so that's its row count. In particular, we're looking for the tuning where [math]\displaystyle{ \frac95 }[/math] and [math]\displaystyle{ \frac{16}{5} }[/math] have exactly equal errors, i.e. they even have the same sign, not opposite signs[note 2]. So to get them to cancel out, we use a -1 as the nonzero entry of one of the two intervals, and conventionally we use 1 for the first one. So with [math]\displaystyle{ \frac95 }[/math] at index 5 of [math]\displaystyle{ \mathrm{T} }[/math] and [math]\displaystyle{ \frac{16}{5} }[/math] at index 6, we find [math]\displaystyle{ K }[/math] = [0 0 0 0 1 -1 0 0].
Now for [math]\displaystyle{ D }[/math]. We know it's a [math]\displaystyle{ (τ - 1, 𝑟) }[/math]-shaped matrix: one row for each tied tuning past the first, and each row is the delta between generator tuning maps, so is [math]\displaystyle{ r }[/math] long like any generator tuning map. In our case we have [math]\displaystyle{ τ = 2 }[/math] and [math]\displaystyle{ r = 2 }[/math], so it's a [math]\displaystyle{ (1,2) }[/math]-shaped matrix. That one row is [math]\displaystyle{ 𝒈_1 - 𝒈_0 }[/math]. So it's {400.865 1903.401] - {399.809 1901.289] = {1.056 2.112].
And that's everything we need to solve!
Work that out and we get [math]\displaystyle{ B }[/math] = [[-0.497891 -0.216260 -0.016343⟩]. (We're showing a little extra precision here than usual.) So we can recover [math]\displaystyle{ 𝒈 }[/math] now as:
[math]\displaystyle{
𝒈 = 𝒈_0 + 𝒓_{0}BD
}[/math]
We haven't worked out [math]\displaystyle{ 𝒓_0 }[/math] yet, but it's [math]\displaystyle{ 𝒕_0 - 𝒋 }[/math], where [math]\displaystyle{ 𝒋 }[/math] = ⟨1200.000 1901.955 2786.314] and [math]\displaystyle{ 𝒕_0 = 𝒈_{0}M }[/math] = {399.809 1901.289][⟨3 0 7] ⟨0 1 0]} = ⟨1199.43 1901.29 2798.66], so [math]\displaystyle{ 𝒓_0 }[/math] = ⟨0.573 0.666 12.349].
Plugging everything in at once could be unwieldy. So let's just do the [math]\displaystyle{ 𝒓_{0}B }[/math] part to find [math]\displaystyle{ 𝒃 }[/math]. We might be curious about that anyway… how close does it match our prediction of about 0.35? Well, it's ⟨0.573 0.666 12.349][[-0.497891 -0.216260 -0.016343⟩] = 0.343. Not bad!
So now plug [math]\displaystyle{ 𝒃 }[/math] into [math]\displaystyle{ 𝒈 = 𝒈_0 + 𝒃D }[/math] and we find [math]\displaystyle{ 𝒈 }[/math] = {399.809 1901.289] + 0.343×{1.056 2.112] = {400.171 1902.011]. And that's what we were looking for!
The ADSLOD here, by the way, is [92.557 92.557 81.117 81.117 57.928 ... ] So it's a tie of 81.117 ¢(C) for the second-most minimax damage to [math]\displaystyle{ \frac95 }[/math] and [math]\displaystyle{ \frac{16}{5} }[/math]. No other tuning can beat this 81.117 number, even just three entries down the list. And so we're done.
Exact solutions with advanced tie-breaking
As for recovering [math]\displaystyle{ G }[/math], though. You know, the whole point of this article—finding exact tunings—we wouldn't want to give up on that just because we had to use advanced tie-breaking, would we?
So we've been looking for [math]\displaystyle{ 𝒈 }[/math] which are blends of other [math]\displaystyle{ 𝒈 }[/math]'s. But we need to look for [math]\displaystyle{ G }[/math]'s that are blends of other [math]\displaystyle{ G }[/math]'s! Doing that directly would explode the dimensionality of the space we're searching, by the rank [math]\displaystyle{ r }[/math] times the length of the blend vector [math]\displaystyle{ 𝒃 }[/math], that is, [math]\displaystyle{ 𝑟×(τ - 1) }[/math]. And would it even be meaningful to independently search the powers of the primes that comprise each entry of a [math]\displaystyle{ 𝒈 }[/math]? Probably not. The compressed information in [math]\displaystyle{ 𝒈 }[/math] is all that really matters for defining the constrained search region. So what if instead we still search by [math]\displaystyle{ 𝒈 }[/math], but what if the blend we find for each [math]\displaystyle{ K }[/math] can be applied to [math]\displaystyle{ G }[/math]'s instead of [math]\displaystyle{ 𝒈 }[/math]'s?
Let's test on an example.
[math]\displaystyle{
G_0 =
\left[ \begin{array} {r}
1 & 0 \\
0 & 0 \\
0 & \frac14 \\
\end{array} \right]
\quad
𝒈_0 =
\left[ \begin{array} {r}
1200.000 & 696.578 \\
\end{array} \right]
\\[20pt]
G_1 =
\left[ \begin{array} {r}
\frac73 & \frac13 \\
-\frac43 & -\frac13 \\
\frac13 & \frac13 \\
\end{array} \right]
\quad
𝒈_1 =
\left[ \begin{array} {r}
1192.831 & 694.786 \\
\end{array} \right]
}[/math]
So [math]\displaystyle{ 𝜹_1 = 𝒈_1 - 𝒈_0 }[/math] = {-7.169 -1.792]. Suppose we get [math]\displaystyle{ 𝒃 }[/math] = [0.5]. We know that [math]\displaystyle{ 𝒃D }[/math] = {-3.584 -0.896]. So [math]\displaystyle{ 𝒈 }[/math] should be [math]\displaystyle{ 𝒈_0 + 𝒃D }[/math] = {1200 696.578] + {-3.584 -0.896] = {1196.416 695.682].
But do we find the same tuning with [math]\displaystyle{ G = G_0 + b_1(G_1 - G_0) + b_2(G_2 - G_0) + \ldots + b_{τ-1}(G_{τ-1} - G_0) }[/math]? That's the key question. (In this case, we have to bust the matrix multiplication up. That is, there's no way to replace the rows of D with entire matrices. Cumbersome, but reality.)
In this case we only have the one delta, [math]\displaystyle{ G_1 - G_0 = }[/math]
[math]\displaystyle{
\left[ \begin{array} {r}
\frac73 & \frac13 \\
-\frac43 & -\frac13 \\
\frac13 & \frac13 \\
\end{array} \right]
-
\left[ \begin{array} {r}
1 & 0 \\
0 & 0 \\
0 & \frac14 \\
\end{array} \right]
=
\left[ \begin{array} {r}
\frac43 & \frac13 \\
-\frac43 & -\frac13 \\
\frac13 & \frac{1}{12} \\
\end{array} \right]
}[/math]
And so [math]\displaystyle{ b_1(G_1 - G_0) }[/math], or half of that, is:
[math]\displaystyle{
\left[ \begin{array} {r}
\frac23 & \frac16 \\
-\frac23 & -\frac16 \\
\frac16 & \frac{1}{24} \\
\end{array} \right]
}[/math]
And add that to [math]\displaystyle{ G_0 }[/math] then:
[math]\displaystyle{
\left[ \begin{array} {r}
1 & 0 \\
0 & 0 \\
0 & \frac14 \\
\end{array} \right]
+
\left[ \begin{array} {r}
\frac23 & \frac16 \\
-\frac23 & -\frac16 \\
\frac16 & \frac{1}{24} \\
\end{array} \right]
=
\left[ \begin{array} {r}
\frac53 & \frac16 \\
-\frac23 & -\frac16 \\
\frac16 & \frac{9}{24} \\
\end{array} \right]
}[/math]
So [math]\displaystyle{ \textbf{g}_1 }[/math] here, the first column, [[math]\displaystyle{ \frac53 }[/math] [math]\displaystyle{ -\frac23 }[/math] [math]\displaystyle{ \frac16 }[/math]⟩, is [math]\displaystyle{ 2^{\frac53}3^{-\frac23}5^{\frac16} \approx 1.996 }[/math]. So [math]\displaystyle{ g_1 }[/math] = 1196.416 ¢.
And [math]\displaystyle{ \textbf{g}_2 }[/math] here, the second column, {{vector|[math]\displaystyle{ \frac16 }[/math] [math]\displaystyle{ -\frac16 }[/math] [math]\displaystyle{ \frac{9}{24} }[/math]⟩, is [math]\displaystyle{ 2^{\frac16}3^{-\frac16}5^{\frac{9}{24}} \approx 1.495 }[/math]. So [math]\displaystyle{ g_2 }[/math] = 695.682 ¢.
Perfect! We wanted {1196.416 695.682] and we got it.
Now maybe this doesn't fully test the system, since we only convexly combined two tunings, but this is probably sound for general use. At least, the test suite of the RTT Library in Wolfram Language included several examples that should have failed upon switching to this way of computing true optimum tunings if this were a problem, but they did not.
Polytope
Keenan Pepper named the file where we wrote the coinciding-damage method "tiptop.py", and along with that coined the tuning scheme name "TIP-TOP", for "Tiebreaker-in-polytope TOP".[note 3] So what's up with this "polytope"?
Polytopes — they're nothing too scary, actually. The name seems imposing, but it's really just a name for a shape which is as generic as possible:
- The "poly-" part means generic to how many vertices/edges/faces/etc. the shape has. This prefix generalizes prefixes you may be familiar with like "tetra-", "penta-", "hexa-", etc., which are used for shapes where we do know exactly how many of the given type of feature a shape has, like a "penta-gon" (5 edges) or a "tetra-hedron" (4 faces).
- The "-tope" part means generic to how many dimensions these features occupy. This suffix generalizes suffixes you may be familiar with like "-gon", "-hedron", "-choron"/"-cell"/"-hedroid", etc., which are used for shapes where we do know exactly how many dimensions a shape occupies, again, like a "hexa-gon" (2 dimensions) or an "octa-hedron" (3 dimensions).
We note that the polytope Keenan refers to is not the full coinciding-damage point set.
It's not even the faceted bowl we see formed from the aerial view by the combination of all the individual target-intervals' tuning damage graph hyper-V's; that's an in-between sized point set we would call the "maximum damage graph vertices".
No, Keenan's polytope refers to an even smaller set of points. It contains either only a single point for the case of an immediately unique optimum, or more than one point which together bound the region (such as a line segment, triangle, etc.) within which the true optimum may be found in the next iteration of the algorithm, using blends. This is what we could call the minimax polytope.
Major modification to Keenan's original method
In the beginning of our discussion of the coinciding-damage method, we mentioned that Douglas Blumeyer had modified Keenan Pepper's algorithm in a way that "simplifies it conceptually and allows it to identify optimum tunings more quickly". Here is an explanation of that change.
Keenan's original design was to only include the zero-damage points once tuning damage space had been reduced to 2D. This design still does eventually finds the same true optimum tunings, but the problem is that it requires advanced tie-breaking to accomplish this, where basic tie-breaking could have worked had those points been included. Consider the case of {2/1,3/1,5/1,6/5} minimax-U blackwood we gave as our basic tie-breaking example here: with Keenan's design, the intersection between [math]\displaystyle{ \frac51 }[/math] and [math]\displaystyle{ \frac65 }[/math]'s creases would not have been included; it would have been as if we were doing the primes minimax-U blackwood example we look at in the following section where basic tie-breaking must fail.
Since advanced tie-breaking requires an entire new iteration of the algorithm, gathering a whole new coinciding-damage point set, it is more computationally expensive than handling a tie-break with the basic technique by simply including some more points in the current iteration.
Also, because always including zero-damage points is a conceptually purer way of presenting the concepts (it doesn't require an entire 7-page separate section to this article explaining the single-free-generator 2D tuning damage space exception, which as you might guess, I did write before having the necessary insight to simplify things), it is preferable for human understanding as well.
I also considered adding the unison to the target-interval set in order to capture the zero-damage points, but that was terribly inefficient and confusing. I also tried generalizing Keenan's code in a different way. Keenan's code includes [math]\displaystyle{ K }[/math] which make a target-interval itself unchanged, but it only does that when the [math]\displaystyle{ K }[/math] have only one column (meaning we're searching 2D tuning damage space). What if we think of it like this: our point set always includes [math]\displaystyle{ K }[/math] which have one column for enforcing a target-interval itself, among any other unchanged-intervals, and we do this not only when [math]\displaystyle{ K }[/math] is one column. That turned out to be an improvement, but it still resulted in redundant points, because we don't need direction permutations for the non-unison target-intervals when their errors are 0 either (e.g. If [math]\displaystyle{ \frac21 }[/math] is pure, and [math]\displaystyle{ \frac61 }[/math] is pure, that implies that [math]\displaystyle{ \frac31 }[/math] is pure. But if [math]\displaystyle{ \frac21 }[/math] is pure, and [math]\displaystyle{ \frac32 }[/math] is pure, that just as well implies that [math]\displaystyle{ \frac31 }[/math] is pure.
Why we abbreviate
For some readers, it may seem pointless, or wasteful, to abbreviate DSLODs like this. Especially in those cases where you're tantalizingly close… you can see that you could break a tie, if only you were allowed to include one more entry in each ADSLOD. Or perhaps you could at least reduce the count of tunings that are tied.
Well, here's another way to think about the reason for this abbreviation, which may help you respect the purpose for the abbreviation. If you falsely eliminate tunings that rightly should still have been tied at this stage, then you will be eliminating tunings that should have been helping to define the boundary of the region to check in the next iteration of the method.
So, if you erroneously reduced the search space down to two tuning points defining a line segment region, you should have been searching an entire triangle-shaped region instead. You might miss the true optimum somewhere in the middle of the area of that triangle, not merely along one of its three sides.
Importance of deduplication
Note that a very good reason to perform the type of deduplication within the target-interval set discussed earlier in this article (here) is to prevent unnecessary failing of the basic tie-breaking mechanism. Suppose we have a case like our basic tie-breakable blackwood example, where two damage graphs' crease is parallel to the floor and forms the minimum of the max damage graph, but we can still look one more position down the ADSLODs to tie-break at some point in the middle of this line segment range which minimizes the third damage position. Well, now imagine that instead we clogged our ADSLODs with a duplicate target-interval, i.e. one whose damage graph is identical to one or the other of these two forming this tied minimax crease. Now we unnecessarily find ourselves with three coinciding damages up top instead of just two, and will be forced to dip into advanced tie-breaking. But if we had only de-duped the target-intervals which map to the same mapped interval up front, we wouldn't have had to do that.
Held-intervals with respect to advanced tie-breaking
In this section we discuss how to handle held-intervals with the coinciding-damage method. We note here that the extra steps involved—allocating columns of the constraint matrices to the held-intervals—are only necessary in the first iteration of the method.
Think of it this way: whichever generators were locked into the appropriate proportion in order to achieve the held-intervals at the top-level, main tuning damage space, those will remain locked in the blends of tunings at lower levels. In other words, whatever cross-section we take to capture the minimax polytope will already be within the held-interval subregion of tuning damage space.
A major flaw with the method
Keenan himself considers his algorithm to be pretty dumb. (It seems sort of fantastically powerful and genius to me, overall, but I can sort of see what he means a bit, now).
One core problem it has causes it to be potentially majorly inefficient, and also somewhat conceptually misleading. That's what we'll discuss here.
When we introduced the concept of blends in an earlier section, we noted how any point within the bounded region can be specified as a blend where the blending variables are positive sum to 1. That's the key thing that keeps us within the region; if we can specify huge and/or negative blending variables, then the exercise is moot, and we can specify points anywhere. Well, if we've got some 1D line segment embedded in a 2D plane, without the sum-to-1 rule, we can use [math]\displaystyle{ \mathbf{A} }[/math] and [math]\displaystyle{ \mathbf{B} }[/math] to describe any point within that 2D plane, anyway.
So, it turns out that this is essentially the same as how it works in advanced tie-breaking. When we take a cross-section of tuning damage space which contains the line segment of our tied basic minimax region, and gather a new coinciding-damage point set in terms of blending variables, we don't know if a point is going to fall within the line segment we care about or not until after we've already computed its blend variables. (Remember, the blend variable for the anchor tuning is always assumed to be whatever is required to get the sum exactly to 1, so all we care about is the other variables summing to something between 0 and 1.)
For example, consider the diagram we showed in this section. Note how the damage graphs for [math]\displaystyle{ \frac52 }[/math] and [math]\displaystyle{ \frac{16}{5} }[/math] intersect (within this cross-section) but just outside the range where [math]\displaystyle{ 0 < b_1 < 1 }[/math]. Well, when we gather our coinciding-damage points, convert their ReDPOTICs and STICs to constraint matrices, and convert those to tunings, it's not until then that we'll realize this tuning is outside of bounds. We could filter it out at this point—it will never qualify as the true optimum tuning, because if you look straight up in the diagram, you can see that the damage to [math]\displaystyle{ \frac95 }[/math] is greater than the basic minimax could potentially be. But we already wasted a lot of resources finding it.
Essentially we search the whole cross-section, not just the minimax polytope we've identified.
And there's no particularly obvious way to rework this method to only find coinciding-damage points for [math]\displaystyle{ K }[/math] where every entry of [math]\displaystyle{ 𝒃 }[/math] is non-negative and [math]\displaystyle{ \llzigzag 𝒃 \rrzigzag_1 = 1 }[/math]. To improve this part of the algorithm would require basically rethinking it from the inside out.
Derivation of extracted anchor
We can derive this formula from the one we had before, like so. Start with:
[math]\displaystyle{
\mathbf{P} = x\mathbf{A} + y\mathbf{B} + z\mathbf{C}
}[/math]
Add [math]\displaystyle{ \mathbf{A} - \mathbf{A} }[/math] to this, which changes nothing.
[math]\displaystyle{
\mathbf{P} = \mathbf{A} - \mathbf{A} + x\mathbf{A} + y\mathbf{B} + z\mathbf{C}
}[/math]
Recognize a coefficient of [math]\displaystyle{ 1 }[/math] on the subtracted [math]\displaystyle{ \mathbf{A} }[/math].
[math]\displaystyle{
\mathbf{P} = \mathbf{A} - 1\mathbf{A} + x\mathbf{A} + y\mathbf{B} + z\mathbf{C}
}[/math]
We know [math]\displaystyle{ x + y + z = 1 }[/math], so we can substitute that in for this [math]\displaystyle{ 1 }[/math].
[math]\displaystyle{
\mathbf{P} = \mathbf{A} - (x + y + z)\mathbf{A} + x\mathbf{A} + y\mathbf{B} + z\mathbf{C}
}[/math]
Distribute.
[math]\displaystyle{
\mathbf{P} = \mathbf{A} - x\mathbf{A} - y\mathbf{A} - z\mathbf{A} + x\mathbf{A} + y\mathbf{B} + z\mathbf{C}
}[/math]
Regroup by [math]\displaystyle{ x }[/math] and [math]\displaystyle{ y }[/math].
[math]\displaystyle{
\mathbf{P} = \mathbf{A} + x(\mathbf{A} - \mathbf{A}) + y(\mathbf{B} - \mathbf{A}) + z(\mathbf{C} - \mathbf{A})
}[/math]
Cancel out these [math]\displaystyle{ \mathbf{A} }[/math]'s and thus [math]\displaystyle{ x }[/math].
[math]\displaystyle{
\mathbf{P} = \mathbf{A} + x(\cancel{\mathbf{A}} - \cancel{\mathbf{A}}) + y(\mathbf{B} - \mathbf{A}) + z(\mathbf{C} - \mathbf{A})
\mathbf{P} = \mathbf{A} + x(0) + y(\mathbf{B} - \mathbf{A}) + z(\mathbf{C} - \mathbf{A})
}[/math]
And so our final formula:
[math]\displaystyle{
\mathbf{P} = \mathbf{A} + y(\mathbf{B} - \mathbf{A}) + z(\mathbf{C} - \mathbf{A})
}[/math]
Equivalence to power-limit approach
In Keenan's original Yahoo groups post, he claims that his method (the core idea of which is explained in a modified form here as the coinciding-damage method) is equivalent to the power-limit method for finding true optimums for minimax tunings: "This is equivalent to taking the limit of the Lp norm minimizer as p tends to infinity (exercise for the reader!)"[note 4]. Douglas Blumeyer has attempted this exercise, but failed. He pestered Keenan himself for the solution, but it had been so long (about 10 years) since Keenan wrote this, he himself could not reproduce. So at this time, this remains an open problem—an exercise for you readers, now.
Normalization required to handle exact tunings
One complication arose with the advanced tie-breaking part of the code (in Dave Keenan & Douglas Blumeyer's RTT Library in Wolfram Language which was adapted from Keenan Pepper's original Python code) upon the switch from Keenan's original technique of computing approximate generator tuning maps [math]\displaystyle{ 𝒈 }[/math] by solving linear systems of equations to Dave Keenan's technique of computing exact generator embeddings [math]\displaystyle{ G }[/math] by doing matrix inverses. In some cases where Keenan's technique used to work fine, Dave's method would fall on its face. Here's what happened.
Essentially, the basic tie-breaking step would come back with a set of tied tunings such as this:
- [math]\displaystyle{ 𝒈_0 }[/math] = {240.000 2786.314]
- [math]\displaystyle{ 𝒈_1 }[/math] = {240.000 2795.336]
- [math]\displaystyle{ 𝒈_2 }[/math] = {240.000 2804.359]
The problem is that when we have three points defining a convex hull, it's supposed to be a triangle! This is a degenerate case where all three points fall along the same line. Not only is this wasteful, but it also screws stuff up, because now there's essentially more than one way to blend [math]\displaystyle{ \mathbf{A} }[/math], [math]\displaystyle{ \mathbf{B} }[/math], and [math]\displaystyle{ \mathbf{C} }[/math] together to get [math]\displaystyle{ \mathbf{P} }[/math], because [math]\displaystyle{ \mathbf{B} }[/math] and [math]\displaystyle{ \mathbf{C} }[/math] pull us away from [math]\displaystyle{ \mathbf{A} }[/math] in the exact same direction.
Note that the only thing that matters is the direction that the tied tunings are from each other, not the distance; the values in the blend map [math]\displaystyle{ 𝒃 }[/math] are continuous and can be anything they need to be to reach a desired point. In other words, all that matters are the proportions of the entries of the deltas to each other. In still other words, different tunings on the same line are redundant.
It happens to be the case here that the [math]\displaystyle{ 𝜹_i }[/math] are not only on the same line, but simple multiples of each other:
- [math]\displaystyle{ 𝜹_2 = 𝒈_1 - 𝒈_0 }[/math] = {240.000 2795.336] - {240.000 2786.314] = {0 9.0225]
- [math]\displaystyle{ 𝜹_1 = 𝒈_2 - 𝒈_0 }[/math] = {240.000 2804.359] - {240.000 2786.314] = {0 18.045]
which is to say that [math]\displaystyle{ 𝒈_1 }[/math] happens to be smack-dab halfway between [math]\displaystyle{ 𝒈_0 }[/math] and [math]\displaystyle{ 𝒈_1 }[/math]. But that's just a distraction; that's not important. It could have been [math]\displaystyle{ ϕ }[/math] of the way between them instead and the problem would have been the same.
Remember that these [math]\displaystyle{ 𝜹_i }[/math] get combined into one big [math]\displaystyle{ D }[/math] matrix. In this case, that's
[math]\displaystyle{
\left[ \begin{array} {r}
0 & 9.0225 \\
0 & 18.045 \\
\end{array} \right]
}[/math]
Using this for [math]\displaystyle{ D }[/math], however, causes every [math]\displaystyle{ B }[/math] we try to find via
[math]\displaystyle{
B = \mathrm{T}WK(DM\mathrm{T}WK)^{-1}
}[/math]
to fail, because the [math]\displaystyle{ DM\mathrm{T}WK }[/math] matrix we try to invert is singular. (And for one reason or another, Keenan's way, using a LinearSolve[], handled this degenerate case without complaining.)
One might think the solution would be simply to canonicalize this [math]\displaystyle{ D }[/math] matrix: HNF it, and delete the all-zero rows. But here's the thing: it's not an integer matrix. It's not even rational. Even though it's seems obvious that since [math]\displaystyle{ 18.045 = 9.0225 × 2 }[/math] we should be able to reduce this thing to:
[math]\displaystyle{
\left[ \begin{array} {r}
0 & 1 \\
0 & 2 \\
\end{array} \right]
}[/math]
actually what we want to do here is different and maybe slightly simpler. At least, it's a different breed of normalization.
What the RTT Library in Wolfram Language does now is Normalize[] every [math]\displaystyle{ 𝜹_i }[/math] to a unit vector and then dedupe them according to if they equal each other or their negation. Given the design of the algorithm, namely, how it doesn't actually restrict itself to searching the convex combination but instead searches the whole affine plane or whatever. And that works.
As a result, however, it doesn't always work directly in blend variables, but in scaled blend variables, scaled by the factor between the normalized and non-normalized deltas. For example, normalizing the above example would create a normalizing size factor of 9.0225. So now the tied minimax range wouldn't be from [math]\displaystyle{ 0 < b_1 < 1 }[/math], but from [math]\displaystyle{ 0 < b_1 < 9.0225 }[/math].
Footnotes
- ↑ You may be unsurprised to learn that this example of basic tie-breaking success was actually developed from the case that requires advanced tie-breaking, i.e. by adding [math]\displaystyle{ \frac65 }[/math] to the target-interval set in order to produce the needed point at the true minimax, rather than the other way around, that the advanced tie-breaking case was developed from the basic example.
- ↑ note that we can't rely on which half of the V-shaped graph we're on to tell whether a damage corresponds with a positive or negative error; that depends on various factors relating to [math]\displaystyle{ 𝒈 }[/math], [math]\displaystyle{ M }[/math], and the ties
- ↑ https://yahootuninggroupsultimatebackup.github.io/tuning-math/topicId_20405.html#20412
- ↑ https://yahootuninggroupsultimatebackup.github.io/tuning-math/topicId_20405.html#20412