Generator embedding optimization: Difference between revisions
Cmloegcmluin (talk | contribs) |
m →With held-intervals: Ref should be note |
||
(24 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
When optimizing tunings of [[regular temperament]]s, it is fairly quick and easy to find ''approximate'' solutions, using (for example) [[Dave Keenan & Douglas Blumeyer's guide to RTT | When optimizing tunings of [[regular temperament]]s, it is fairly quick and easy to find ''approximate'' solutions, using (for example) [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#General method|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. | 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 | 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 embedding matrix|generator embedding]]s, i.e. lists of [[prime-count vector]]s, 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 map]]s, 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. | 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 embedding matrix|generator embedding]]s, i.e. lists of [[prime-count vector]]s, 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 map]]s, 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= | = Intro = | ||
== A summary of the methods == | |||
==A summary of the methods== | The three biggest sections of this article are dedicated to three specialized tuning methods, one for each of the three special [[optimization power]]s: the [[pseudoinverse]] method is used for <math>p = 2</math> ([[miniRMS]] tuning schemes), the zero-damage method is used for <math>p = 1</math> ([[miniaverage]] tuning schemes), and the coinciding-damage method is used for <math>p = ∞</math> ([[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning fundamentals#Common_interpretations|minimax]] tuning schemes). | ||
The three biggest sections of this article are dedicated to three specialized tuning methods, one for each of the three special [[optimization power]]s: the [[pseudoinverse]] method is used for <math>p = 2</math> ([[miniRMS]] tuning schemes), the zero-damage method is used for <math>p = 1</math> ([[miniaverage]] tuning schemes), and the coinciding-damage method is used for <math>p = ∞</math> ([[ | |||
These three methods also work for [[all-interval tuning scheme]]s, which by definition are all minimax tuning schemes (optimization power <math>∞</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>q</math> which directly determines the method used, but rather its [[dual power]], <math>\text{dual}(q)</math>: the power of the [[dual norm]] minimized on the [[retuning magnitude]]. So the pseudoinverse method is used for <math>\text{dual}(q) = 2</math>, the zero-damage method is used for <math>\text{dual}(q) = 1</math>, and the coinciding-damage method is used for <math>\text{dual}(q) = ∞</math>. | These three methods also work for [[all-interval tuning scheme]]s, which by definition are all minimax tuning schemes (optimization power <math>∞</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>q</math> which directly determines the method used, but rather its [[dual power]], <math>\text{dual}(q)</math>: the power of the [[dual norm]] minimized on the [[retuning magnitude]]. So the pseudoinverse method is used for <math>\text{dual}(q) = 2</math>, the zero-damage method is used for <math>\text{dual}(q) = 1</math>, and the coinciding-damage method is used for <math>\text{dual}(q) = ∞</math>. | ||
Line 19: | Line 17: | ||
The general method ''also'' works for those special powers <math>1</math>, <math>2</math>, and <math>∞</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). | The general method ''also'' works for those special powers <math>1</math>, <math>2</math>, and <math>∞</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== | == Exact vs. approximate solutions == | ||
Tuning computation methods can be classified by whether they give an approximate or exact solution. | Tuning computation methods can be classified by whether they give an approximate or exact solution. | ||
Line 29: | Line 26: | ||
We can calculate <math>𝒈</math> from this <math>G</math> via <math>𝒋G</math>, that is, the generator tuning map is obtained as the product of the just tuning map and the generator embedding. | We can calculate <math>𝒈</math> from this <math>G</math> via <math>𝒋G</math>, that is, the generator tuning map is obtained as the product of the just tuning map and the generator embedding. | ||
Because <math>𝒈 = 𝒋G</math>, if <math>𝒈</math> is the primary target, not <math>G</math>, and a formula for <math>G</math> is known, then it is possible to substitute that into <math>𝒈 = 𝒋G</math> and thereby bypass explicitly solving for <math>G</math>. For example, this was essentially what was done in the [[ | Because <math>𝒈 = 𝒋G</math>, if <math>𝒈</math> is the primary target, not <math>G</math>, and a formula for <math>G</math> is known, then it is possible to substitute that into <math>𝒈 = 𝒋G</math> and thereby bypass explicitly solving for <math>G</math>. For example, this was essentially what was done in the [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Only held-intervals method|Only-held intervals method]] and [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Pseudoinverse_method_for_.5Bmath.5D.F0.9D.91.9D.3D2.5B.2Fmath.5D|Pseudoinverse method]] sections of [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation|D&D's guide: Tuning computation]]). | ||
Note that with any exact type that solves for <math>G</math>, since it is possible to have an exact <math>𝒋</math>, it is also possible to find an exact <math>𝒈</math>. For example, the approximate value of the 5-limit <math>𝒋</math> we're quite familiar with is {{map|1200.000 1901.955 2786.314}}, but its exact value is {{map|<math>1200×\log_2(2)</math> <math>1200×\log_2(3)</math> <math>1200×\log_2(5)</math>}}, so if the exact tuning of quarter-comma meantone is <math>G</math> = {{rbra|{{vector|1 0 0}} {{vector|0 0 ¼}}}}, then this can be expressed as an exact generator tuning map <math>𝒈</math> = {{rbra|<math>(1200×\log_2(2))(1) + (1200×\log_2(3))(0) + (1200×\log_2(5))(0)</math> <math>(1200×\log_2(2))(0) + (1200×\log_2(3))(0) + (1200×\log_2(5))(\frac14)</math>}} = {{rbra|<math>1200</math> <math>\dfrac{1200×\log_2(5)}{4}</math>}}. | Note that with any exact type that solves for <math>G</math>, since it is possible to have an exact <math>𝒋</math>, it is also possible to find an exact <math>𝒈</math>. For example, the approximate value of the 5-limit <math>𝒋</math> we're quite familiar with is {{map|1200.000 1901.955 2786.314}}, but its exact value is {{map|<math>1200×\log_2(2)</math> <math>1200×\log_2(3)</math> <math>1200×\log_2(5)</math>}}, so if the exact tuning of quarter-comma meantone is <math>G</math> = {{rbra|{{vector|1 0 0}} {{vector|0 0 ¼}}}}, then this can be expressed as an exact generator tuning map <math>𝒈</math> = {{rbra|<math>(1200×\log_2(2))(1) + (1200×\log_2(3))(0) + (1200×\log_2(5))(0)</math> <math>(1200×\log_2(2))(0) + (1200×\log_2(3))(0) + (1200×\log_2(5))(\frac14)</math>}} = {{rbra|<math>1200</math> <math>\dfrac{1200×\log_2(5)}{4}</math>}}. | ||
Line 38: | Line 35: | ||
{| class="wikitable center-all" | {| class="wikitable center-all" | ||
|- | |- | ||
|<math>2</math> | ! Optimization power | ||
| | ! Method | ||
| | ! Solution type | ||
|<math>G</math> | ! Solves for | ||
|- | |||
| <math>2</math> | |||
| Pseudoinverse | |||
| Exact | |||
| <math>G</math> | |||
|- | |- | ||
|<math>1</math> | | <math>1</math> | ||
| | | Zero-damage | ||
| | | Exact | ||
|<math>G</math> | | <math>G</math> | ||
|- | |- | ||
|<math>∞</math> | | <math>∞</math> | ||
| | | Coinciding-damage | ||
| | | Exact | ||
|<math>G</math> | | <math>G</math> | ||
|- | |- | ||
| rowspan="2" | | | rowspan="2" | General | ||
| | | Power | ||
| rowspan="2" | | | rowspan="2" | Approximate | ||
| rowspan="2" |<math>𝒈</math> | | rowspan="2" | <math>𝒈</math> | ||
|- | |- | ||
|power limit | | power limit | ||
|- | |- | ||
| | | N/A | ||
| | | Only held-intervals | ||
| | | Exact | ||
|<math>G</math> | | <math>G</math> | ||
|} | |} | ||
==The generator embedding== | == The generator embedding == | ||
Roughly speaking, if <math>M</math> is the matrix which isolates the ''temperament'' information, and <math>𝒋</math> is the matrix which isolates the ''sizing'' information, then <math>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: | Roughly speaking, if <math>M</math> is the matrix which isolates the ''temperament'' information, and <math>𝒋</math> is the matrix which isolates the ''sizing'' information, then <math>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: | ||
Line 102: | Line 98: | ||
==Algebraic setup== | == Algebraic setup == | ||
The basic algebraic setup of tuning optimization looks like this: | The basic algebraic setup of tuning optimization looks like this: | ||
Line 128: | Line 123: | ||
But | But wait—there are actually two more matrices we haven't recognized yet, on the just side of things. These are <math>G_{\text{j}}</math> and <math>M_{\text{j}}</math>. Unsurprisingly, these two are closely related to <math>G</math> and <math>M</math>, respectively. The subscript <math>\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>I</math>, an ''[[ | We could replace either or both of these matrices with <math>I</math>, an ''[[Dave Keenan & Douglas Blumeyer's guide to RTT/Exploring temperaments#Nullspace|identity matrix]]''. On account of both <math>G_{\text{j}}</math> and <math>M_{\text{j}}</math> being identity matrices, we can eliminate them from our expression | ||
Line 154: | Line 149: | ||
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 {{vector|1 0 0}}, a representation of the interval <math>\frac21</math>; the vector for the second generator is {{vector|0 1 0}}, a representation of the interval <math>\frac31</math>; and the vector for the third generator is {{vector|0 0 1}}, a representation of the interval <math>\frac51</math>. | 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 {{vector|1 0 0}}, a representation of the interval <math>\frac21</math>; the vector for the second generator is {{vector|0 1 0}}, a representation of the interval <math>\frac31</math>; and the vector for the third generator is {{vector|0 0 1}}, a representation of the interval <math>\frac51</math>. | ||
We can even understand this in terms of a units analysis, where if <math>M_{\text{j}}</math> is taken to have units of '''g'''/'''p''', and <math>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>\small \sf p</math>'s and <math>\small \sf g</math>'s canceling out; for more details, see [[Dave Keenan & Douglas Blumeyer's guide to RTT | We can even understand this in terms of a units analysis, where if <math>M_{\text{j}}</math> is taken to have units of '''g'''/'''p''', and <math>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>\small \sf p</math>'s and <math>\small \sf g</math>'s canceling out; for more details, see [[Dave Keenan & Douglas Blumeyer's guide to RTT/Units analysis#The JI mapping times the JI generators embedding|here]]. | ||
So when the interval vectors constituting the target-interval list <math>\mathrm{T}</math> are multiplied by <math>G_{\text{j}}M_{\text{j}}</math> they are unchanged, which means that multiplying the result by <math>𝒋</math> simply computes their just sizes. | So when the interval vectors constituting the target-interval list <math>\mathrm{T}</math> are multiplied by <math>G_{\text{j}}M_{\text{j}}</math> they are unchanged, which means that multiplying the result by <math>𝒋</math> simply computes their just sizes. | ||
==Deduplication== | == Deduplication == | ||
=== Between target-interval set and held-interval basis === | |||
===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. | 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. | ||
Line 168: | Line 161: | ||
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. | 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=== | === 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. | 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 {{rket|{{map|2 3 5 6}} {{map|0 1 -2 -2}}}}, if the octave is held unchanged, then there is no sense keeping <math>\frac75</math> in the target-interval set. The octave {{ket|1 0 0 0}} maps to {{rket|2 0}} in this temperament, and <math>\frac75</math> {{ket|0 0 -1 1}} maps to {{rket|1 0}}. So if the first generator is fixed in order to hold the octave unchanged, then ~<math>\frac75</math>'s tuning will also be fixed. | For example, in pajara temperament, with mapping {{rket|{{map|2 3 5 6}} {{map|0 1 -2 -2}}}}, if the octave is held unchanged, then there is no sense keeping <math>\frac75</math> in the target-interval set. The octave {{ket|1 0 0 0}} maps to {{rket|2 0}} in this temperament, and <math>\frac75</math> {{ket|0 0 -1 1}} maps to {{rket|1 0}}. So if the first generator is fixed in order to hold the octave unchanged, then ~<math>\frac75</math>'s tuning will also be fixed. | ||
===Within target-interval set=== | === 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>\frac{10}{7}</math> and <math>\frac75</math>, but pajara maps both of those intervals to {{rket|1 0}}, and thus the damage to these two intervals will always be the same. | 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>\frac{10}{7}</math> and <math>\frac75</math>, but pajara maps both of those intervals to {{rket|1 0}}, and thus the damage to these two intervals will always be the same. | ||
Line 182: | Line 173: | ||
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>k</math> and the count of deduped mapped target-intervals, which would require a new variable. | 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>k</math> and the count of deduped mapped target-intervals, which would require a new variable. | ||
=Only held-intervals method= | = 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>h</math> reaches the size of our generator count, also known as [[rank]] <math>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>\mathrm{H}</math>, but an [[unchanged-interval basis|''unchanged-interval'' basis]] <math>\mathrm{U}</math>. | |||
==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 | |||
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>\mathrm{U}</math>, not <math>\mathrm{H}</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>\mathrm{U}</math>, not <math>\mathrm{H}</math> | ||
==Generator embedding== | == Generator embedding == | ||
So, substituting <math>\mathrm{U}</math> in for <math>\mathrm{H}</math> in the formula we learned [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Only held-intervals method|from the D&D's guide article]]: | |||
So, substituting <math>\mathrm{U}</math> in for <math>\mathrm{H}</math> in the formula we learned from the D&D's guide article: | |||
Line 225: | Line 213: | ||
</math> | </math> | ||
= Pseudoinverse method = | |||
=Pseudoinverse method= | Similarly, we can take the pseudoinverse formula as presented in [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Pseudoinverse method]], substitute <math>𝒋G</math> for <math>𝒈</math>, and cancel out: | ||
Similarly, we can take the pseudoinverse formula as presented in [[Dave Keenan & Douglas Blumeyer's guide to RTT | |||
Line 241: | Line 227: | ||
==Connection with the only held-intervals method== | == Connection with the only held-intervals method == | ||
Note the similarity between the pseudoinverse formula <math>A^{+} = A^\mathsf{T}(AA^\mathsf{T})^{-1}</math> and the only held-interval interval <math>G = 𝒋\mathrm{U}(M\mathrm{U})^{-1}</math>; in fact, it's the same formula, if we simply substitute in <math>M^\mathsf{T}</math> for <math>\mathrm{U}</math>. | Note the similarity between the pseudoinverse formula <math>A^{+} = A^\mathsf{T}(AA^\mathsf{T})^{-1}</math> and the only held-interval interval <math>G = 𝒋\mathrm{U}(M\mathrm{U})^{-1}</math>; in fact, it's the same formula, if we simply substitute in <math>M^\mathsf{T}</math> for <math>\mathrm{U}</math>. | ||
Line 270: | Line 255: | ||
or in other words, the two held-intervals are {{vector|1 1 0}} and {{vector|0 1 4}}, which as ratios are <math>\frac61</math> and <math>\frac{1875}{1}</math>, respectively. Those may seem like some pretty strange intervals to be unchanged, for sure, but there is a way to think about it that makes it seem less strange. This tells us that whatever the error is on <math>\frac21</math>, it is the negation of the error on <math>\frac31</math>, because when those intervals are combined, we get a pure <math>\frac61</math>. This also tells us that whatever the error is on <math>\frac31</math>, that it in turn is the negation of the error on <math>\frac{625}{1} = \frac{5^4}{1}</math>.<ref>Gene Ward Smith discovering this relationship: https://yahootuninggroupsultimatebackup.github.io/tuning-math/topicId_16172#16172</ref> Also, remember that these intervals form a ''basis'' for the held-intervals; any interval that is a linear combination of them is also unchanged. | or in other words, the two held-intervals are {{vector|1 1 0}} and {{vector|0 1 4}}, which as ratios are <math>\frac61</math> and <math>\frac{1875}{1}</math>, respectively. Those may seem like some pretty strange intervals to be unchanged, for sure, but there is a way to think about it that makes it seem less strange. This tells us that whatever the error is on <math>\frac21</math>, it is the negation of the error on <math>\frac31</math>, because when those intervals are combined, we get a pure <math>\frac61</math>. This also tells us that whatever the error is on <math>\frac31</math>, that it in turn is the negation of the error on <math>\frac{625}{1} = \frac{5^4}{1}</math>.<ref group="note">Gene Ward Smith discovering this relationship: https://yahootuninggroupsultimatebackup.github.io/tuning-math/topicId_16172#16172</ref> Also, remember that these intervals form a ''basis'' for the held-intervals; any interval that is a linear combination of them is also unchanged. | ||
As another example, the unchanged-interval of the primes miniRMS-U tuning of 12-ET would be {{vector|12 19 28}}. Don't mistake that for the 12-ET map {{map|12 19 28}}; that's the prime-count vector you get from transposing it! That interval, while rational and thus theoretically JI, could not be heard directly by humans, considering that <math>2^{12}3^{19}5^{28}</math> is over 107 octaves above unison and would typically call for scientific notation to express; it's 128553.929 ¢, which is exactly 1289 (<math>= 12^2+19^2+28^2</math>) iterations of the 99.732 ¢ generator for this tuning. | As another example, the unchanged-interval of the primes miniRMS-U tuning of 12-ET would be {{vector|12 19 28}}. Don't mistake that for the 12-ET map {{map|12 19 28}}; that's the prime-count vector you get from transposing it! That interval, while rational and thus theoretically JI, could not be heard directly by humans, considering that <math>2^{12}3^{19}5^{28}</math> is over 107 octaves above unison and would typically call for scientific notation to express; it's 128553.929 ¢, which is exactly 1289 (<math>= 12^2+19^2+28^2</math>) iterations of the 99.732 ¢ generator for this tuning. | ||
==Example== | == Example == | ||
Let's refer back to the example given in [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Plugging back in]], picking up from this point: | |||
Let's refer back to the example given in [[Dave Keenan & Douglas Blumeyer's guide to RTT | |||
Line 351: | Line 335: | ||
The matrices with shapes <math>(3, 8)(8, 2)(2, 2)</math> led us to a <math>(3, \cancel{8})(\cancel{8}, \cancel{2})(\cancel{2}, 2) = (3, 2)</math>-shaped matrix, and that's just what we want in a <math>G</math> here. Specifically, we want a <math>(d, r)</math>-shaped matrix, one that will convert <math>(r, 1)</math>-shaped generator-count | The matrices with shapes <math>(3, 8)(8, 2)(2, 2)</math> led us to a <math>(3, \cancel{8})(\cancel{8}, \cancel{2})(\cancel{2}, 2) = (3, 2)</math>-shaped matrix, and that's just what we want in a <math>G</math> here. Specifically, we want a <math>(d, r)</math>-shaped matrix, one that will convert <math>(r, 1)</math>-shaped generator-count vectors—those that are results of mapping <math>(d, 1)</math>-shaped [[prime-count vector]]s by the temperament mapping matrix—back into <math>(d, 1)</math>-shaped prime-count vectors, but now representing the intervals as they sound under this tuning of this temperament. | ||
And so we've found what we were looking for, <math>G = \mathrm{T}C(M\mathrm{T}C)^\mathsf{T}(M\mathrm{T}C(M\mathrm{T}C)^\mathsf{T})^{-1}</math>. | And so we've found what we were looking for, <math>G = \mathrm{T}C(M\mathrm{T}C)^\mathsf{T}(M\mathrm{T}C(M\mathrm{T}C)^\mathsf{T})^{-1}</math>. | ||
Line 394: | Line 378: | ||
</math> | </math> | ||
==Pseudoinverse: | == Pseudoinverse: The "how" == | ||
Here we will investigate ''how'', mechanically speaking, the pseudoinverse almost magically takes us straight to that answer we want. | Here we will investigate ''how'', mechanically speaking, the pseudoinverse almost magically takes us straight to that answer we want. | ||
===Like an inverse=== | === Like an inverse === | ||
As you might suppose—given a name like ''pseudo''inverse—this thing is ''like'' a normal matrix inverse, but not ''exactly''. True inverses are only defined for ''square'' matrices, so the pseudoinverse is essentially a way to make something similar available for non-square i.e. ''rectangular'' matrices. This is useful for RTT because the <math>M\mathrm{T}W</math> matrices we use it on are usually rectangular; they are always <math>(r, k)</math>-shaped matrices. | |||
As you might | |||
But why would we want to take the inverse of <math>M\mathrm{T}W</math> in the first place, though? To understand this, it will help to first simplify the problem. | But why would we want to take the inverse of <math>M\mathrm{T}W</math> in the first place, though? To understand this, it will help to first simplify the problem. | ||
Line 407: | Line 389: | ||
At this point we're left with simply <math>M</math>. And this is still a rectangular matrix; it's <math>(r, d)</math>-shaped. So if we want to invert it, we'll only be able to pseudoinvert it. But we're still in the dark about why we would ever want to invert it. | At this point we're left with simply <math>M</math>. And this is still a rectangular matrix; it's <math>(r, d)</math>-shaped. So if we want to invert it, we'll only be able to pseudoinvert it. But we're still in the dark about why we would ever want to invert it. | ||
To finally get to understanding why, let's look to an expression discussed here: [[ | To finally get to understanding why, let's look to an expression discussed here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Basic_algebraic_setup|Basic algebraic setup]]: | ||
Line 419: | Line 401: | ||
So given some mapping <math>M</math>, which <math>G</math> makes that happen? Well, based on the above, it should be the inverse of <math>M</math>! That's because anything times its own inverse equals an identity, i.e. <math>M^{-1}M = I</math>. | So given some mapping <math>M</math>, which <math>G</math> makes that happen? Well, based on the above, it should be the inverse of <math>M</math>! That's because anything times its own inverse equals an identity, i.e. <math>M^{-1}M = I</math>. | ||
===Definition of inverse=== | === Definition of inverse === | ||
Multiplying by something to give an identity is, in fact, the very definition of "inverse". To illustrate, here's an example of a true inverse, in the case of <math>(2, 2)</math>-shaped matrices: | Multiplying by something to give an identity is, in fact, the very definition of "inverse". To illustrate, here's an example of a true inverse, in the case of <math>(2, 2)</math>-shaped matrices: | ||
Line 461: | Line 442: | ||
But the problem is, as we know already, that <math>M^{-1}</math> doesn't exist, because <math>M</math> is a rectangular matrix. That's why we use its pseudoinverse <math>M^{+}</math> instead. Or to be absolutely clear, we choose our generator embedding <math>G</math> to be <math>M^{+}</math>. | But the problem is, as we know already, that <math>M^{-1}</math> doesn't exist, because <math>M</math> is a rectangular matrix. That's why we use its pseudoinverse <math>M^{+}</math> instead. Or to be absolutely clear, we choose our generator embedding <math>G</math> to be <math>M^{+}</math>. | ||
===Sometimes an inverse=== | === Sometimes an inverse === | ||
Now to be completely accurate, when we multiply a rectangular matrix by its pseudoinverse, we ''can'' also get an identity matrix, but only if we do it a certain way. (And this fact that we can get an identity matrix at all is a critical example of the way how the pseudoinverse provides inverse''-like'' powers for rectangular matrices.) But there are still a few key differences between this situation and the situation of a square matrix and its true inverse: | Now to be completely accurate, when we multiply a rectangular matrix by its pseudoinverse, we ''can'' also get an identity matrix, but only if we do it a certain way. (And this fact that we can get an identity matrix at all is a critical example of the way how the pseudoinverse provides inverse''-like'' powers for rectangular matrices.) But there are still a few key differences between this situation and the situation of a square matrix and its true inverse: | ||
# The first big difference is that in the case of square matrices, as we saw a moment ago, all the matrices have the same shape. However, for a non-square (rectangular) matrix with shape <math>(m, n)</math>, it will have a pseudoinverse with shape <math>(n, m)</math>. This difference perhaps could have gone without saying. | # The first big difference is that in the case of square matrices, as we saw a moment ago, all the matrices have the same shape. However, for a non-square (rectangular) matrix with shape <math>(m, n)</math>, it will have a pseudoinverse with shape <math>(n, m)</math>. This difference perhaps could have gone without saying. | ||
# The second big difference is that in the case of square matrices, the multiplication order is irrelevant: you can either left-multiply the original matrix by its inverse or right-multiply it, and either way, you'll get the same identity matrix. But there's no way you could get the same identity matrix in the case of a rectangular matrix and its pseudoinverse; an <math>(m, n)</math>-shaped matrix times an <math>(n, m)</math>-shaped matrix gives an <math>(m, m)</math>-shaped matrix, while an <math>(n, m)</math>-shaped matrix times an <math>(m, n)</math>-shaped matrix gives an <math>(n, n)</math>-shaped matrix (the inner height and width always have to match, and the resulting matrix always has shape matching the outer width and height). So: either way we will get a square matrix, but one way we get an <math>(m, m)</math> shape, and the other way we get an <math>(n, n)</math> shape. | # The second big difference is that in the case of square matrices, the multiplication order is irrelevant: you can either left-multiply the original matrix by its inverse or right-multiply it, and either way, you'll get the same identity matrix. But there's no way you could get the same identity matrix in the case of a rectangular matrix and its pseudoinverse; an <math>(m, n)</math>-shaped matrix times an <math>(n, m)</math>-shaped matrix gives an <math>(m, m)</math>-shaped matrix, while an <math>(n, m)</math>-shaped matrix times an <math>(m, n)</math>-shaped matrix gives an <math>(n, n)</math>-shaped matrix (the inner height and width always have to match, and the resulting matrix always has shape matching the outer width and height). So: either way we will get a square matrix, but one way we get an <math>(m, m)</math> shape, and the other way we get an <math>(n, n)</math> shape. | ||
# The third big | # The third big difference—and this is probably the most important one, but we had to build up to it by looking at the other two big differences first—is that only one of those two possible results of multiplying a rectangular matrix by its pseudoinverse will actually even give an identity matrix! It will be the one of the two that gives the ''smaller'' square matrix. | ||
=== Example of when the pseudoinverse behaves like a true inverse === | |||
Here's an example with meantone temperament as <math>M</math>. Its pseudoinverse <math>M^{+} = M^\mathsf{T}(MM^\mathsf{T})^{-1}</math> is {{rbra|{{vector|17 16 -4}} {{vector|16 17 4}}}}/33. First, we'll look at the multiplication order that gives an identity matrix, when the <math>(2, 3)</math>-shaped rectangular matrix right-multiplied by its <math>(3, 2)</math>-shaped rectangular pseudoinverse gives a <math>(2, 2)</math>-shaped square identity matrix: | Here's an example with meantone temperament as <math>M</math>. Its pseudoinverse <math>M^{+} = M^\mathsf{T}(MM^\mathsf{T})^{-1}</math> is {{rbra|{{vector|17 16 -4}} {{vector|16 17 4}}}}/33. First, we'll look at the multiplication order that gives an identity matrix, when the <math>(2, 3)</math>-shaped rectangular matrix right-multiplied by its <math>(3, 2)</math>-shaped rectangular pseudoinverse gives a <math>(2, 2)</math>-shaped square identity matrix: | ||
Line 508: | Line 487: | ||
Let's give an RTT way to interpret this first result. Basically it tells us that <math>M^{+}</math> might be a reasonable generator embedding <math>G</math> for this temperament. First of all, let's note that <math>M</math> was not specifically designed to handle non-JI intervals like those represented by the prime-count vector columns of <math>M^{+}</math>, like we are making it do here. But we can get away with it anyway. And in this case, <math>M</math> maps the first column of <math>M^{+}</math> to the generator-count vector {{rket|1 0}}, and its second column to the generator-count vector {{rket|0 1}}; we can find these two vectors as the columns of the identity matrix <math>I</math>. | Let's give an RTT way to interpret this first result. Basically it tells us that <math>M^{+}</math> might be a reasonable generator embedding <math>G</math> for this temperament. First of all, let's note that <math>M</math> was not specifically designed to handle non-JI intervals like those represented by the prime-count vector columns of <math>M^{+}</math>, like we are making it do here. But we can get away with it anyway. And in this case, <math>M</math> maps the first column of <math>M^{+}</math> to the generator-count vector {{rket|1 0}}, and its second column to the generator-count vector {{rket|0 1}}; we can find these two vectors as the columns of the identity matrix <math>I</math>. | ||
Now, one fact we can take from this is that the first column of <math>M^{+}</math> | Now, one fact we can take from this is that the first column of <math>M^{+}</math>—the non-JI vector {{vector|<math>\frac{17}{33}</math> <math>\frac{16}{33}</math> <math>\frac{-4}{33}</math>}}—shares at least one thing in common with other JI intervals such as <math>\frac21</math> {{vector|1 0 0}}, <math>\frac{81}{40}</math> {{vector|-3 4 -1}}, and <math>\frac{160}{81}</math> {{vector|5 -4 1}}: they all get mapped to {{rket|1 0}} by this meantone mapping matrix <math>M</math>. Note that this is no ''guarantee'' that {{vector|<math>\frac{17}{33}</math> <math>\frac{16}{33}</math> <math>\frac{-4}{33}</math>}} is close to these intervals (in theory, we can add or subtract an indefinite number of temperament commas from an interval without altering what it maps to!), but it at least ''suggests'' that it's reasonably close to them, i.e. that it's about an octave in size. | ||
And a similar statement can be made about the second column vector of <math>M^{+}</math>, {{vector|<math>\frac{16}{33}</math> <math>\frac{17}{33}</math> <math>\frac{4}{33}</math>}}, with respect to <math>\frac31</math> {{vector|0 1 0}} and <math>\frac{80}{27}</math> {{vector|4 -3 1}}, etc.: they all map to {{rket|0 1}}, and so {{vector|<math>\frac{16}{33}</math> <math>\frac{17}{33}</math> <math>\frac{4}{33}</math>}} is ''probably'' about a perfect twelfth in size like the rest of them. | And a similar statement can be made about the second column vector of <math>M^{+}</math>, {{vector|<math>\frac{16}{33}</math> <math>\frac{17}{33}</math> <math>\frac{4}{33}</math>}}, with respect to <math>\frac31</math> {{vector|0 1 0}} and <math>\frac{80}{27}</math> {{vector|4 -3 1}}, etc.: they all map to {{rket|0 1}}, and so {{vector|<math>\frac{16}{33}</math> <math>\frac{17}{33}</math> <math>\frac{4}{33}</math>}} is ''probably'' about a perfect twelfth in size like the rest of them. | ||
Line 514: | Line 493: | ||
(In this case, both likelihoods are indeed true: our two tuned generators are 1202.607 ¢ and 696.741 ¢ in size.) | (In this case, both likelihoods are indeed true: our two tuned generators are 1202.607 ¢ and 696.741 ¢ in size.) | ||
===Example of when the pseudoinverse does not behave like a true inverse=== | === Example of when the pseudoinverse does not behave like a true inverse === | ||
Before we get to that, we should finish what we've got going here, and show for contrast what happens when we flip-flop <math>M</math> and <math>M^{+}</math>, so that the <math>(3, 2)</math>-shaped rectangular pseudoinverse times the original <math>(2, 3)</math>-shaped rectangular matrix leads to a <math>(3, 3)</math>-shaped matrix which is ''not'' an identity matrix: | Before we get to that, we should finish what we've got going here, and show for contrast what happens when we flip-flop <math>M</math> and <math>M^{+}</math>, so that the <math>(3, 2)</math>-shaped rectangular pseudoinverse times the original <math>(2, 3)</math>-shaped rectangular matrix leads to a <math>(3, 3)</math>-shaped matrix which is ''not'' an identity matrix: | ||
Line 552: | Line 530: | ||
While this matrix <math>M^{+}M</math> clearly isn't an identity matrix, since it's not all zeros except for ones running along its main diagonal, and it doesn't really look anything like an identity matrix from a superficial | While this matrix <math>M^{+}M</math> clearly isn't an identity matrix, since it's not all zeros except for ones running along its main diagonal, and it doesn't really look anything like an identity matrix from a superficial perspective—just judging by the numbers we can read off its entries—it turns out that ''behavior-wise'' this matrix does actually work out to be as "close" to an identity matrix as we can get, at least in a certain sense. And since our goal with tuning this temperament was to approximate JI as closely as possible, from this certain mathematical perspective, this is the matrix that accomplishes that. But again, we'll get to why exactly this matrix is the one that accomplishes that in a little bit. | ||
=== Un-simplifying === | |||
First, to show how we can un-simplify things. The insight leading to this choice of <math>G = M^{+}</math> was made under the simplifying circumstances of <math>W = I</math> (unity-weight damage) and <math>\mathrm{T} = \mathrm{T}_{\text{p}} = I</math> (primes as target-intervals). But nothing about those choices of <math>W</math> or <math>\mathrm{T}</math> affect how this method ''works''; setting them to <math>I</math> was only to help us humans ''see'' the way forward. There's nothing stopping us now from using any other weights and target-intervals for <math>W</math> and <math>\mathrm{T}</math>; the concept behind this method holds. Choosing <math>G = \mathrm{T}W(M\mathrm{T}W)^{+}</math>, that is, still finds for us the <math>p = 2</math> optimization for the problem. | First, to show how we can un-simplify things. The insight leading to this choice of <math>G = M^{+}</math> was made under the simplifying circumstances of <math>W = I</math> (unity-weight damage) and <math>\mathrm{T} = \mathrm{T}_{\text{p}} = I</math> (primes as target-intervals). But nothing about those choices of <math>W</math> or <math>\mathrm{T}</math> affect how this method ''works''; setting them to <math>I</math> was only to help us humans ''see'' the way forward. There's nothing stopping us now from using any other weights and target-intervals for <math>W</math> and <math>\mathrm{T}</math>; the concept behind this method holds. Choosing <math>G = \mathrm{T}W(M\mathrm{T}W)^{+}</math>, that is, still finds for us the <math>p = 2</math> optimization for the problem. | ||
===Demystifying the formula=== | === Demystifying the formula === | ||
One way to think about what's happening in the formula of the pseudoinverse uses a technique we might call the "transform-act-antitransform technique": we want to take some action, but we can't do it in the current state, so we transform into a state where we ''can'', then we take the action, and we finish off by performing the opposite of the initial transformation so that we get back to more of a similar state to the one we began with, yet having accomplished the action we intended. | One way to think about what's happening in the formula of the pseudoinverse uses a technique we might call the "transform-act-antitransform technique": we want to take some action, but we can't do it in the current state, so we transform into a state where we ''can'', then we take the action, and we finish off by performing the opposite of the initial transformation so that we get back to more of a similar state to the one we began with, yet having accomplished the action we intended. | ||
Line 571: | Line 547: | ||
[[File:Why mapping times its transpose is invertible.png|none|thumbnail|500x500px]] | [[File:Why mapping times its transpose is invertible.png|none|thumbnail|500x500px]] | ||
==Pseudoinverse: | == Pseudoinverse: The "why" == | ||
In the previous section we took a look at ''how'', mechanically, the pseudoinverse gives the solution for optimization power <math>p = 2</math>. As for ''why'', conceptually speaking, the pseudoinverse gives us the minimum point for the RMS graph in tuning damage space, it's sort of just one of those seemingly miraculously useful mathematical results. But we can try to give a basic explanation here. | In the previous section we took a look at ''how'', mechanically, the pseudoinverse gives the solution for optimization power <math>p = 2</math>. As for ''why'', conceptually speaking, the pseudoinverse gives us the minimum point for the RMS graph in tuning damage space, it's sort of just one of those seemingly miraculously useful mathematical results. But we can try to give a basic explanation here. | ||
===Derivative, for slope=== | === Derivative, for slope === | ||
First, let's briefly go over some math facts. For some readers, these will be review: | First, let's briefly go over some math facts. For some readers, these will be review: | ||
* The slope of a graph means its rate of change. When slope is positive, the graph is going up, and when negative, it's going down. | * The slope of a graph means its rate of change. When slope is positive, the graph is going up, and when negative, it's going down. | ||
Line 584: | Line 558: | ||
So, considering that we want to find the minimum of a graph, one approach should be to find the derivative of this graph, then find the point(s) where its value is 0, which is where the slope is 0. This means those are the possible points where we have a local minimum, which means therefore that those are the points where we maybe have a global minimum, which is what we're after. | So, considering that we want to find the minimum of a graph, one approach should be to find the derivative of this graph, then find the point(s) where its value is 0, which is where the slope is 0. This means those are the possible points where we have a local minimum, which means therefore that those are the points where we maybe have a global minimum, which is what we're after. | ||
===A unique minimum=== | === A unique minimum === | ||
As discussed in the tuning fundamentals article (in the section [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning fundamentals#Power continuum|Non-unique tunings – power continuum]]), the graphs of mean damage and max damage—which are equivalent to the power means with powers <math>p = 1</math> and <math>p = ∞</math>, respectively—consist of straight line segments connected by sharp corners, while all other optimization powers between <math>1</math> and <math>∞</math> form smooth curves. This is important because it is only for graphs with smooth curves that we can use its derivative to find the minimum point; the sharp corners of the other type of graph create discontinuities at those points, which in this context means points which have no definitive slope. The simple mathematical methods we use to find slope for smooth graphs get all confused and crash or give wrong results if we try to use them on these types of graphs. | |||
As discussed in the tuning fundamentals article (in the section [[Dave Keenan & Douglas Blumeyer's guide to RTT | |||
So we can use the derivative slope technique for other powers <math>1 < p < ∞</math>, but the pseudoinverse will only match the solution when <math>p = 2</math>. | So we can use the derivative slope technique for other powers <math>1 < p < ∞</math>, but the pseudoinverse will only match the solution when <math>p = 2</math>. | ||
Line 592: | Line 565: | ||
And, spoiler alert: another key thing that's true about the <math>2</math>-mean graph whose minimum point we seek: it has only one point where the slope is equal 0, and it's our global minimum. Again, this is true of any of our curved <math>p</math>-mean graphs, but we only really care about it in the case of <math>p = 2</math>. | And, spoiler alert: another key thing that's true about the <math>2</math>-mean graph whose minimum point we seek: it has only one point where the slope is equal 0, and it's our global minimum. Again, this is true of any of our curved <math>p</math>-mean graphs, but we only really care about it in the case of <math>p = 2</math>. | ||
===A toy example using the derivative=== | === A toy example using the derivative === | ||
To get our feet on solid ground, let's just work through the math for an [[equal temperament]] example, i.e. one with only a single generator. | To get our feet on solid ground, let's just work through the math for an [[equal temperament]] example, i.e. one with only a single generator. | ||
Kicking off with the setup discussed [[ | Kicking off with the setup discussed [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Basic_algebraic_setup|here]], we have: | ||
Line 877: | Line 849: | ||
Ta-da! There's our generator size: 100.234 ¢.<ref>The actual answer is more like 100.236. The result here is due to compounding rounding errors that I was too lazy to account for when preparing these materials. Sorry about that. ~Douglas</ref> | Ta-da! There's our generator size: 100.234 ¢.<ref group="note">The actual answer is more like 100.236. The result here is due to compounding rounding errors that I was too lazy to account for when preparing these materials. Sorry about that. ~Douglas</ref> | ||
=== Verifying the toy example with the pseudoinverse === | |||
Okay... but what the heck does this have to do with a pseudoinverse? Well, for a sanity check, let's double-check against our pseudoinverse method. | Okay... but what the heck does this have to do with a pseudoinverse? Well, for a sanity check, let's double-check against our pseudoinverse method. | ||
Line 1,066: | Line 1,037: | ||
When we work through that, we get 100.236 ¢. Close enough (shrugging off rounding errors). So we've sanity-checked at least. | When we work through that, we get 100.236 ¢. Close enough (shrugging off rounding errors). So we've sanity-checked at least. | ||
But if we really want to see the connection between the pseudoinverse and the finding the zero of the | But if we really want to see the connection between the pseudoinverse and the finding the zero of the derivative—how they both find the point where the slope of the RMS graph is zero and therefore it is at its minimum—we're going to have to upgrade from an equal temperament (rank-1 temperament) to a rank-2 temperament. In other words, we need to address tunings with ''more than one generator'', ones can't be represented by a simple scalar anymore, but instead need to be represented with a vector. | ||
=== A demonstration using matrix calculus === | |||
Technically speaking, even with two generators, meaning two variables, we could take the derivative with respect to one, and then take the derivative with respect to the other. And with three generators we could take three derivatives. But this gets out of hand. And there's a cleverer way we can think about the problem, which involves treating the vector containing all the generators as a single variable. We can do that! But it involves matrix calculus. And in this section we'll work through how. | Technically speaking, even with two generators, meaning two variables, we could take the derivative with respect to one, and then take the derivative with respect to the other. And with three generators we could take three derivatives. But this gets out of hand. And there's a cleverer way we can think about the problem, which involves treating the vector containing all the generators as a single variable. We can do that! But it involves matrix calculus. And in this section we'll work through how. | ||
Line 1,143: | Line 1,113: | ||
Alright! We're finally ready to surface <math>𝒈</math>. It's been hiding in <math>𝒕</math> all along; the tuning map is equal to the generator tuning map times the mapping, i.e. <math>𝒕 = 𝒈M</math>. So we can just substitute that in everywhere. Exactly what we'll do is <math>𝖙 = 𝒕\mathrm{T}W = (𝒈M)\mathrm{T}W = 𝒈(M\mathrm{T}W) = 𝒈𝔐</math>, that last step introducing a new Fraktur-style symbol.<ref>Ideally we'd've consistently applied the Fraktur-styling effect to each of these letters, changing no other properties, i.e. ended up with an uppercase italic M and lowercase bold italic j and t, but unfortunately a consistent effect was not available using Unicode and the wiki's <math>\LaTeX</math> abilities, a consistent effect, anyway, that also satisfactorily captured the compound aspect of what these things represent.</ref> | Alright! We're finally ready to surface <math>𝒈</math>. It's been hiding in <math>𝒕</math> all along; the tuning map is equal to the generator tuning map times the mapping, i.e. <math>𝒕 = 𝒈M</math>. So we can just substitute that in everywhere. Exactly what we'll do is <math>𝖙 = 𝒕\mathrm{T}W = (𝒈M)\mathrm{T}W = 𝒈(M\mathrm{T}W) = 𝒈𝔐</math>, that last step introducing a new Fraktur-style symbol.<ref group="note">Ideally we'd've consistently applied the Fraktur-styling effect to each of these letters, changing no other properties, i.e. ended up with an uppercase italic M and lowercase bold italic j and t, but unfortunately a consistent effect was not available using Unicode and the wiki's <math>\LaTeX</math> abilities, a consistent effect, anyway, that also satisfactorily captured the compound aspect of what these things represent.</ref> | ||
Line 1,169: | Line 1,139: | ||
Well, now we've come to it. We've run out of things we can do without confronting the question: how in the world do we take derivatives of matrices? This next part is going to require some of that matrix calculus we warned about. Fortunately, if one is previously familiar with normal algebraic differentiation rules, these will not seem too wild: | Well, now we've come to it. We've run out of things we can do without confronting the question: how in the world do we take derivatives of matrices? This next part is going to require some of that matrix calculus we warned about. Fortunately, if one is previously familiar with normal algebraic differentiation rules, these will not seem too wild: | ||
# The last term, <math>𝖏𝖏^\mathsf{T}</math>, is going to vanish, because with respect to <math>𝒈</math>, it's a constant; there's no factor of <math>𝒈</math> in it. | # The last term, <math>𝖏𝖏^\mathsf{T}</math>, is going to vanish, because with respect to <math>𝒈</math>, it's a constant; there's no factor of <math>𝒈</math> in it. | ||
# The middle term, <math>-2𝖏𝔐^\mathsf{T}𝒈^\mathsf{T}</math>, has a single factor of <math>𝒈</math>, so it will remain but with that factor gone. (Technically it's a factor of <math>𝒈^\mathsf{T}</math>, but for reasons that would probably require a deeper understanding of the subtleties of matrix calculus than the present author commands, it works out this way anyway. Perhaps we should have differentiated instead with respect to <math>𝒈^\mathsf{T}</math>, rather than <math>𝒈</math>?)<ref>Perhaps re-running this process in the recognition of the fact that these matrices are shorthand for an underlying system of equations, and the derivative of <math>𝒈</math> is, in fact, its gradient, or in other words, the vector of partial derivatives with respect to each of its entries (as discussed in more detail in the later section, [[#Multiple derivatives]]), we could nail this down.</ref> | # The middle term, <math>-2𝖏𝔐^\mathsf{T}𝒈^\mathsf{T}</math>, has a single factor of <math>𝒈</math>, so it will remain but with that factor gone. (Technically it's a factor of <math>𝒈^\mathsf{T}</math>, but for reasons that would probably require a deeper understanding of the subtleties of matrix calculus than the present author commands, it works out this way anyway. Perhaps we should have differentiated instead with respect to <math>𝒈^\mathsf{T}</math>, rather than <math>𝒈</math>?)<ref group="note">Perhaps re-running this process in the recognition of the fact that these matrices are shorthand for an underlying system of equations, and the derivative of <math>𝒈</math> is, in fact, its gradient, or in other words, the vector of partial derivatives with respect to each of its entries (as discussed in more detail in the later section, [[#Multiple derivatives]]), we could nail this down.</ref> | ||
# The first term, <math>𝒈𝔐𝔐^\mathsf{T}𝒈^\mathsf{T}</math>, can in a way be seen to have a <math>𝒈^2</math>, because it contains both a <math>𝒈</math> as well as a <math>𝒈^\mathsf{T}</math> (and we demonstrated earlier how for a vector <math>\textbf{v}</math>, there is a relationship between itself squared and it times its transpose); so, just as an <math>x^2</math> differentiates to a <math>2x</math>, that is, the power is reduced by 1 and multiplies into any existing coefficient, this term becomes <math>2𝒈𝔐𝔐^\mathsf{T}</math>. | # The first term, <math>𝒈𝔐𝔐^\mathsf{T}𝒈^\mathsf{T}</math>, can in a way be seen to have a <math>𝒈^2</math>, because it contains both a <math>𝒈</math> as well as a <math>𝒈^\mathsf{T}</math> (and we demonstrated earlier how for a vector <math>\textbf{v}</math>, there is a relationship between itself squared and it times its transpose); so, just as an <math>x^2</math> differentiates to a <math>2x</math>, that is, the power is reduced by 1 and multiplies into any existing coefficient, this term becomes <math>2𝒈𝔐𝔐^\mathsf{T}</math>. | ||
Line 1,223: | Line 1,193: | ||
If you're hungry for more information on these concepts, or even just another take on it, please see [[User:Sintel/Generator optimization#Least squares method]]. | If you're hungry for more information on these concepts, or even just another take on it, please see [[User:Sintel/Generator optimization#Least squares method]]. | ||
==With held-intervals== | == With held-intervals == | ||
The pseudoinverse method can be adapted to handle tuning schemes which have held-intervals. The basic idea here is that we can no longer simply grab the tuning found as the point at the bottom of the tuning damage graph bowl hovering above the floor, because that tuning probably doesn't also happen to be one that leaves the requested interval unchanged. We can imagine an additional feature in our tuning damage space: the line across this bowl which connects every point where the generator tunings work out such that our interval is indeed unchanged. Again, this line probably doesn't straight through the bottommost point of our RMS-damage graph. But that's okay. That just means we ''could'' still decrease the overall damage further if we didn't hold the interval unchanged. But assuming we're serious about holding this interval unchanged, we've simply modified the problem a bit. Now we're looking for the point along this new held-interval line which is closest to the floor. Simple enough to understand, in concept! The rest of this section is dedicated to explaining how, mathematically speaking, we're able to identify that point. It still involves matrix calculus—derivatives of vectors, and such—but now we also pull in some additional ideas. We hope you dig it.<ref group="note">If you don't dig it, please consider alternative attempts to explain these ideas here: [[User:Sintel/Generator_optimization#Constraints]], here: [[Constrained_tuning/Analytical_solution_to_constrained_Euclidean_tunings]], and here: [[Target tuning#Least squares tunings]]</ref> | |||
The pseudoinverse method can be adapted to handle tuning schemes which have held-intervals. The basic idea here is that we can no longer simply grab the tuning found as the point at the bottom of the tuning damage graph bowl hovering above the floor, because that tuning probably doesn't also happen to be one that leaves the requested interval unchanged. We can imagine an additional feature in our tuning damage space: the line across this bowl which connects every point where the generator tunings work out such that our interval is indeed unchanged. Again, this line probably doesn't straight through the bottommost point of our RMS-damage graph. But that's okay. That just means we ''could'' still decrease the overall damage further if we didn't hold the interval unchanged. But assuming we're serious about holding this interval unchanged, we've simply modified the problem a bit. Now we're looking for the point along this new held-interval line which is closest to the floor. Simple enough to understand, in concept! The rest of this section is dedicated to explaining how, mathematically speaking, we're able to identify that point. It still involves matrix | |||
We'll be talking through this problem assuming a three-dimensional tuning damage graph, which is to say, we're dealing with a rank-2 temperament (the two generator dimensions across the floor, and the damage dimension up from the floor). If we asked for more than one interval to be held unchanged, then we'd flip over to the "only held-intervals" method discussed later, because at that point there's only a single possible tuning. And if we asked for less than one interval to be held unchanged, then we'd be back to the ordinary pseudoinverse method which you've already learned. So for this extended example we'll be assuming one held-interval. But the principles discussed here generalize to higher dimensions of temperaments and more held-intervals, if the dimensionality supports them. These higher dimensional examples are more difficult to visualize, though, of course, and so we've chosen the simplest possibility that sufficiently demonstrates the ideas we need to learn. | We'll be talking through this problem assuming a three-dimensional tuning damage graph, which is to say, we're dealing with a rank-2 temperament (the two generator dimensions across the floor, and the damage dimension up from the floor). If we asked for more than one interval to be held unchanged, then we'd flip over to the "only held-intervals" method discussed later, because at that point there's only a single possible tuning. And if we asked for less than one interval to be held unchanged, then we'd be back to the ordinary pseudoinverse method which you've already learned. So for this extended example we'll be assuming one held-interval. But the principles discussed here generalize to higher dimensions of temperaments and more held-intervals, if the dimensionality supports them. These higher dimensional examples are more difficult to visualize, though, of course, and so we've chosen the simplest possibility that sufficiently demonstrates the ideas we need to learn. | ||
===Topographic view=== | === Topographic view === | ||
<math> | <math> | ||
% Latex equivalents of the wiki templates llzigzag and rrzigzag for double zigzag brackets. | % Latex equivalents of the wiki templates llzigzag and rrzigzag for double zigzag brackets. | ||
Line 1,268: | Line 1,237: | ||
Next, we need to figure out how to identify this point. It may seem frustrating, because we're looking right at it! But we don't already have formulas for these contour lines. | Next, we need to figure out how to identify this point. It may seem frustrating, because we're looking right at it! But we don't already have formulas for these contour lines. | ||
===Matching slopes=== | === Matching slopes === | ||
In order to identify this point, it's going to be more helpful to look at the entire graph of our held-interval's error. That is, rather than only drawing the line where it's zero: | In order to identify this point, it's going to be more helpful to look at the entire graph of our held-interval's error. That is, rather than only drawing the line where it's zero: | ||
Line 1,302: | Line 1,270: | ||
Now, we're not attempting to distinguish the sizes of these slopes here. We could do that, perhaps by changing the relative scale of the arrows. But that's particularly important for our purposes. We only need to notice the different ''directions'' these slopes point. | Now, we're not attempting to distinguish the sizes of these slopes here. We could do that, perhaps by changing the relative scale of the arrows. But that's particularly important for our purposes. We only need to notice the different ''directions'' these slopes point. | ||
You may recall that in the simpler | You may recall that in the simpler case—with no held-intervals—we identified the point at the bottom of the bowl using derivatives; this point is where the derivative (slope) is equal to zero. Well, what can we notice about the point we're seeking to identify? It's where the slopes of the RMS damage graph for the target-intervals and the error of the held-interval match! | ||
[[File:Held-interval pseudoinverse topographic 6.png|frameless|600x600px]] | [[File:Held-interval pseudoinverse topographic 6.png|frameless|600x600px]] | ||
Line 1,325: | Line 1,293: | ||
But there's another special way of asking for the same thing, that isn't as obvious-looking, but consolidates it all down to a single equation, | But there's another special way of asking for the same thing, that isn't as obvious-looking, but consolidates it all down to a single equation, which—due to some mathemagic—eventually works out to give us a really nice solution. Here's what that looks like: | ||
Line 1,333: | Line 1,301: | ||
What we've done here is added a new variable <math>λ</math><ref>This is a different lambda to the one conventionally used for eigenvalues, or as we call them, scaling factors. This lambda refers to Lagrange, the mathematician who developed this technique.</ref>, a multiplier which scales the error in the interval we want to be unchanged. We can visualize its effect as saying: we don't care about the relative lengths of these two vectors; we only care about wherever they point in exactly the same direction. This trick works as long as we take the derivative with respect to <math>λ</math> as well, which you'll note we're doing now too.<ref>To help develop your intuition for these sorts of problems, we recommend Grant Sanderson's series of videos for Khan Academy's YouTube channel, about Lagrange multipliers for constrained optimizations: https://www.youtube.com/playlist?list=PLCg2-CTYVrQvNGLbd-FN70UxWZSeKP4wV</ref> We don't expect this to be clear straight away; the reason this works will probably only become clear in later steps of working through the problem. | What we've done here is added a new variable <math>λ</math><ref group="note">This is a different lambda to the one conventionally used for eigenvalues, or as we call them, scaling factors. This lambda refers to Lagrange, the mathematician who developed this technique.</ref>, a multiplier which scales the error in the interval we want to be unchanged. We can visualize its effect as saying: we don't care about the relative lengths of these two vectors; we only care about wherever they point in exactly the same direction. This trick works as long as we take the derivative with respect to <math>λ</math> as well, which you'll note we're doing now too.<ref group="note">To help develop your intuition for these sorts of problems, we recommend Grant Sanderson's series of videos for Khan Academy's YouTube channel, about Lagrange multipliers for constrained optimizations: https://www.youtube.com/playlist?list=PLCg2-CTYVrQvNGLbd-FN70UxWZSeKP4wV</ref> We don't expect this to be clear straight away; the reason this works will probably only become clear in later steps of working through the problem. | ||
Let's rework our equation a bit to make things nicer. One thing we can do is put both terms on one side of the equation, equalling zero (rather, the zero vector, with a bolded zero): | Let's rework our equation a bit to make things nicer. One thing we can do is put both terms on one side of the equation, equalling zero (rather, the zero vector, with a bolded zero): | ||
Line 1,387: | Line 1,355: | ||
Everything in that expression other than <math>𝒈</math> and <math>λ</math> are known values; only <math>𝒈</math> and <math>λ</math> are variables. | Everything in that expression other than <math>𝒈</math> and <math>λ</math> are known values; only <math>𝒈</math> and <math>λ</math> are variables. | ||
As a final change, we're going to recognize the fact that for higher-dimensional temperaments, we might sometimes have multiple held-intervals. Which is to say that our new variable might actually itself be a vector! So we'll use a bold <math>\textbf{λ}</math> here to capture that idea.<ref>See https://en.m.wikipedia.org/wiki/Lagrange_multiplier#Multiple_constraints for more information.</ref> (The example we will demonstrate with periodically will still only have one held-interval, though, but that's fine if this is a one-entry vector, whose only entry is <math>λ_1</math>.) Note that we need to locate <math>\textbf{λ}</math> on the right side of each term now, so that its <math>h</math> height matches up with the <math>h</math> width of <math>\mathrm{H}</math>. | As a final change, we're going to recognize the fact that for higher-dimensional temperaments, we might sometimes have multiple held-intervals. Which is to say that our new variable might actually itself be a vector! So we'll use a bold <math>\textbf{λ}</math> here to capture that idea.<ref group="note">See https://en.m.wikipedia.org/wiki/Lagrange_multiplier#Multiple_constraints for more information.</ref> (The example we will demonstrate with periodically will still only have one held-interval, though, but that's fine if this is a one-entry vector, whose only entry is <math>λ_1</math>.) Note that we need to locate <math>\textbf{λ}</math> on the right side of each term now, so that its <math>h</math> height matches up with the <math>h</math> width of <math>\mathrm{H}</math>. | ||
Line 1,397: | Line 1,365: | ||
Now in the simpler case, when we took the derivative simply with respect to <math>𝒈</math>, we could almost treat the vectors and matrices like normal variables when taking derivatives: exponents came down as coefficients, and exponents decremented by 1. But now that we're taking the derivative with respect to both <math>𝒈</math> and <math>\textbf{λ}</math>, the clearest way forward is to understand this in terms of a system of equations, rather than a single equation of matrices and vectors. | Now in the simpler case, when we took the derivative simply with respect to <math>𝒈</math>, we could almost treat the vectors and matrices like normal variables when taking derivatives: exponents came down as coefficients, and exponents decremented by 1. But now that we're taking the derivative with respect to both <math>𝒈</math> and <math>\textbf{λ}</math>, the clearest way forward is to understand this in terms of a system of equations, rather than a single equation of matrices and vectors. | ||
===Multiple derivatives=== | === Multiple derivatives === | ||
One way of thinking about what we're asking for with <math>\dfrac{\partial}{\partial{𝒈, \textbf{λ}}}</math> is that we want the vector whose entries are partial derivatives with respect to each scalar entry of <math>𝒈</math> and <math>\textbf{λ}</math>. We hinted at this earlier when we introduced the bold-zero vector <math>\textbf{0}</math>, which represented a zero for each generator. So if: | One way of thinking about what we're asking for with <math>\dfrac{\partial}{\partial{𝒈, \textbf{λ}}}</math> is that we want the vector whose entries are partial derivatives with respect to each scalar entry of <math>𝒈</math> and <math>\textbf{λ}</math>. We hinted at this earlier when we introduced the bold-zero vector <math>\textbf{0}</math>, which represented a zero for each generator. So if: | ||
Line 1,467: | Line 1,434: | ||
To give a quick and dirty answer to the question posed earlier regarding why introducing <math>\textbf{λ}</math> is a replacement of any sort for the obvious equation <math>𝒓\mathrm{H} = 0</math>, notice what the derivative of the third equation will be. We'll work it out in rigorous detail soon, but for now, let's just observe how <math>\dfrac{\partial}{\partial{λ_1}}(\frac12𝒈𝔐𝔐^\mathsf{T}𝒈^\mathsf{T} - 𝖏𝔐^\mathsf{T}𝒈^\mathsf{T} + \frac12𝖏𝖏^\mathsf{T} + 𝒈M\mathrm{H}\textbf{λ} - 𝒋\mathrm{H}\textbf{λ}) = 𝒈M\mathrm{H} - 𝒋\mathrm{H}</math>. So if that's equal to 0, and <math>𝒓</math> can be rewritten as <math>𝒕 - 𝒋</math> and further as <math>𝒈𝑀 - 𝒋</math>, then we can see how this has covered our bases re: <math>𝒓\mathrm{H} = 0</math>, while also providing the connective tissue to the other equations re: using <math>𝒈</math> and <math>\textbf{λ}</math> to minimize damage to our target-intervals; because <math>\textbf{λ}</math> figures in terms in the first two equations which also have a <math>𝒈</math> in them, so whatever it comes out to will affect those; this is how we achieve the offsetting from the actual bottom of the damage bowl. | To give a quick and dirty answer to the question posed earlier regarding why introducing <math>\textbf{λ}</math> is a replacement of any sort for the obvious equation <math>𝒓\mathrm{H} = 0</math>, notice what the derivative of the third equation will be. We'll work it out in rigorous detail soon, but for now, let's just observe how <math>\dfrac{\partial}{\partial{λ_1}}(\frac12𝒈𝔐𝔐^\mathsf{T}𝒈^\mathsf{T} - 𝖏𝔐^\mathsf{T}𝒈^\mathsf{T} + \frac12𝖏𝖏^\mathsf{T} + 𝒈M\mathrm{H}\textbf{λ} - 𝒋\mathrm{H}\textbf{λ}) = 𝒈M\mathrm{H} - 𝒋\mathrm{H}</math>. So if that's equal to 0, and <math>𝒓</math> can be rewritten as <math>𝒕 - 𝒋</math> and further as <math>𝒈𝑀 - 𝒋</math>, then we can see how this has covered our bases re: <math>𝒓\mathrm{H} = 0</math>, while also providing the connective tissue to the other equations re: using <math>𝒈</math> and <math>\textbf{λ}</math> to minimize damage to our target-intervals; because <math>\textbf{λ}</math> figures in terms in the first two equations which also have a <math>𝒈</math> in them, so whatever it comes out to will affect those; this is how we achieve the offsetting from the actual bottom of the damage bowl. | ||
===Break down matrices=== | === Break down matrices === | ||
In order to work this out, though, we'll need to break our occurrences of <math>𝒈</math> down into <math>g_1</math> and <math>g_2</math> (and <math>\textbf{λ}</math> down into <math>λ_1</math>). | In order to work this out, though, we'll need to break our occurrences of <math>𝒈</math> down into <math>g_1</math> and <math>g_2</math> (and <math>\textbf{λ}</math> down into <math>λ_1</math>). | ||
Line 1,695: | Line 1,661: | ||
<math> | <math> | ||
\small | |||
\begin{array} {c} | \begin{array} {c} | ||
f(𝒈, \textbf{λ}) & = & \frac12\mathrm{a}_{11}g_1^2 & + & \frac12(\mathrm{a}_{12} + \mathrm{a}_{21})g_1g_2 & + & \frac12\mathrm{a}_{22}g_2^2 & - & \mathrm{b}_{11}g_1 & - & \mathrm{b}_{12}g_2 & + & \frac12\mathrm{c}_{11} & + & \mathrm{d}_{11}g_1λ_1 & + & \mathrm{d}_{12}g_2λ_1 & - & \mathrm{e}_{11}λ_1 & \\ | f(𝒈, \textbf{λ}) & = & \frac12\mathrm{a}_{11}g_1^2 & + & \frac12(\mathrm{a}_{12} + \mathrm{a}_{21})g_1g_2 & + & \frac12\mathrm{a}_{22}g_2^2 & - & \mathrm{b}_{11}g_1 & - & \mathrm{b}_{12}g_2 & + & \frac12\mathrm{c}_{11} & + & \mathrm{d}_{11}g_1λ_1 & + & \mathrm{d}_{12}g_2λ_1 & - & \mathrm{e}_{11}λ_1 & \\ | ||
Line 1,721: | Line 1,689: | ||
</math> | </math> | ||
=== Build matrices back up === | |||
===Build matrices back up=== | |||
In this section we'd like to work our way back from this rather clunky and tedious system of equations situation back to matrices. As our first step, let's space our derivative equations' terms out nicely so we can understand better the relationships between them: | In this section we'd like to work our way back from this rather clunky and tedious system of equations situation back to matrices. As our first step, let's space our derivative equations' terms out nicely so we can understand better the relationships between them: | ||
Line 1,804: | Line 1,770: | ||
The replacements for <math>\mathrm{B}</math> and <math>\mathrm{D}</math> may seem obvious enough, but you may initially balk at the replacement of <math>\mathrm{A}</math> here, but there's a reason that works. It's due to the fact that the thing <math>\mathrm{A}</math> represents is the product of a thing and its own transpose, which means entries mirrored across the main diagonal are equal to each other. So if <math>\mathrm{a}_{12} = \mathrm{a}_{21}</math>, then <math>\frac12(\mathrm{a}_{12} + \mathrm{a}_{21}) = \mathrm{a}_{12} = \mathrm{a}_{21}</math>. Feel free to check this yourself, or compare with our work-through in the footnote here.<ref> | The replacements for <math>\mathrm{B}</math> and <math>\mathrm{D}</math> may seem obvious enough, but you may initially balk at the replacement of <math>\mathrm{A}</math> here, but there's a reason that works. It's due to the fact that the thing <math>\mathrm{A}</math> represents is the product of a thing and its own transpose, which means entries mirrored across the main diagonal are equal to each other. So if <math>\mathrm{a}_{12} = \mathrm{a}_{21}</math>, then <math>\frac12(\mathrm{a}_{12} + \mathrm{a}_{21}) = \mathrm{a}_{12} = \mathrm{a}_{21}</math>. Feel free to check this yourself, or compare with our work-through in the footnote here.<ref group="note"> | ||
<math> | <math> | ||
Line 2,030: | Line 1,996: | ||
And so, without held-intervals, the generators can be found as the pseudoinverse of <math>M\mathrm{T}W</math> (left-multiplied by <math>\mathrm{T}W</math>), with generators, they can be found as almost the same thing, just with some augmentations to the matrices. This augmentation results in an extra value at the end, <math>λ_1</math>, but we don't need it and can just discard it. Ta da! | And so, without held-intervals, the generators can be found as the pseudoinverse of <math>M\mathrm{T}W</math> (left-multiplied by <math>\mathrm{T}W</math>), with generators, they can be found as almost the same thing, just with some augmentations to the matrices. This augmentation results in an extra value at the end, <math>λ_1</math>, but we don't need it and can just discard it. Ta da! | ||
===Hardcoded example=== | === Hardcoded example === | ||
At this point everything on the right side of this equation is known. Let's actually plug in some numbers to convince ourselves this makes sense. Suppose we go with an unchanged octave, porcupine temperament, the 6-TILT, and unity-weight damage (and of course, optimization power <math>2</math>). Then we have: | At this point everything on the right side of this equation is known. Let's actually plug in some numbers to convince ourselves this makes sense. Suppose we go with an unchanged octave, porcupine temperament, the 6-TILT, and unity-weight damage (and of course, optimization power <math>2</math>). Then we have: | ||
<math> | <math> | ||
\small | |||
\begin{array} {c} | \begin{array} {c} | ||
Line 2,129: | Line 2,095: | ||
As for <math>\mathrm{T}W</math>, that's easy, because <math>W</math> | As for <math>\mathrm{T}W</math>, that's easy, because <math>W</math>—being a unity-weight matrix—is an identity matrix, so it's equal simply to <math>\mathrm{T}</math>. But regarding <math>M\mathrm{T}W = M\mathrm{T}</math>, that would be helpful to compute in advance: | ||
Line 2,379: | Line 2,345: | ||
And that's all there is to it.<ref>Writes the present author, Douglas Blumeyer, who is relieved to have completely demystified this process for himself, after being daunted by it for over a year, then struggling for a solid week to assemble it from the hints left by the better-educated tuning theorists who came before me.</ref> | And that's all there is to it.<ref group="note">Writes the present author, Douglas Blumeyer, who is relieved to have completely demystified this process for himself, after being daunted by it for over a year, then struggling for a solid week to assemble it from the hints left by the better-educated tuning theorists who came before me.</ref> | ||
==For all-interval tuning schemes== | == For all-interval tuning schemes == | ||
So far we've looked at how to use the linear algebra operation called the pseudoinverse to compute miniRMS tunings. We can use a variation of that approach to solve [[Euclideanized]] [[all-interval tuning scheme]]s. So where miniRMS tuning schemes are those with the optimization power <math>p</math> is equal to <math>2</math>, all-interval minimax-ES tuning schemes are those with the dual norm power <math>\text{dual}(q)</math> equal to <math>2</math>. | |||
So far we've looked at how to use the linear algebra operation called the pseudoinverse to compute miniRMS tunings. We can use a variation of that approach to solve [[Euclideanized]] [[all-interval tuning]]s. So where miniRMS tuning schemes are those with the optimization power <math>p</math> is equal to <math>2</math>, all-interval minimax-ES tuning schemes are those with the dual norm power <math>\text{dual}(q)</math> equal to <math>2</math>. | |||
=== Setup === | |||
The pseudoinverse of a matrix <math>A</math> is notated as <math>A^{+}</math>, and for convenience, here's its equation again: | The pseudoinverse of a matrix <math>A</math> is notated as <math>A^{+}</math>, and for convenience, here's its equation again: | ||
Line 2,419: | Line 2,383: | ||
===Example=== | === Example === | ||
So suppose we want the minimax-ES tuning of meantone temperament, where <math>M</math> = {{rket|{{map|1 1 0}} {{map|0 1 4}}}} and <math>C_{\text{p}} = L</math>. Basically we just need to compute <math>MS_{\text{p}}</math>: | So suppose we want the minimax-ES tuning of meantone temperament, where <math>M</math> = {{rket|{{map|1 1 0}} {{map|0 1 4}}}} and <math>C_{\text{p}} = L</math>. Basically we just need to compute <math>MS_{\text{p}}</math>: | ||
Line 2,520: | Line 2,483: | ||
And when you multiply that by <math>𝒋</math>, we get the generator tuning map <math>𝒈</math> for the minimax-ES tuning of meantone, {{map|1201.397 697.049}}. | And when you multiply that by <math>𝒋</math>, we get the generator tuning map <math>𝒈</math> for the minimax-ES tuning of meantone, {{map|1201.397 697.049}}. | ||
==With alternative complexities== | == With alternative complexities == | ||
The following examples all pick up from a shared setup here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Alternative complexities#Computing all-interval tuning schemes with alternative complexities]]. | |||
The following examples all pick up from a shared setup here: [[Dave Keenan & Douglas Blumeyer's guide to RTT | |||
For all complexities used here (well again at least the first several more basic ones), our formula will be: | For all complexities used here (well again at least the first several more basic ones), our formula will be: | ||
Line 2,532: | Line 2,494: | ||
===Minimax-E-S=== | === Minimax-E-S === | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Alternative complexities#Log-product2]]. Plugging <math>L^{-1}</math> into our pseudoinverse method for <math>S_{\text{p}}</math> we find: | |||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT | |||
Line 2,610: | Line 2,571: | ||
This too can be computed easily with the Wolfram Library: | This too can be computed easily with the Wolfram Library: | ||
<pre> | |||
In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-ES"] | In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-ES"] | ||
Out: {1199.562 163.891] </ | Out: {1199.562 163.891] </pre> | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT | === Minimax-E-sofpr-S === | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Alternative complexities#Sum-of-prime-factors-with-repetition2]]. Plugging <math>\text{diag}(𝒑)^{-1}</math> into our pseudoinverse method for <math>S_{\text{p}}</math> we find: | |||
Line 2,691: | Line 2,651: | ||
This too can be computed easily with the Wolfram Library: | This too can be computed easily with the Wolfram Library: | ||
<pre> | |||
In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-E-sopfr-S"] | In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-E-sopfr-S"] | ||
Out: {1199.567 164.102] </ | Out: {1199.567 164.102] </pre> | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT | === Minimax-E-copfr-S === | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Alternative complexities#Count-of-prime-factors-with-repetition2]]. Plugging <math>I</math> into our pseudoinverse method for <math>S_{\text{p}}</math> we find: | |||
Line 2,762: | Line 2,721: | ||
This too can be computed easily with the Wolfram Library: | This too can be computed easily with the Wolfram Library: | ||
<pre> | |||
In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-E-copfr-S"] | In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-E-copfr-S"] | ||
Out: {1198.595 162.737] </ | Out: {1198.595 162.737] </pre> | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT | === Minimax-E-lils-S === | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Alternative complexities#Log-integer-limit-squared2]]. | |||
As for the minimax-E-lils-S tuning, we use the pseudoinverse method, but with the same augmented matrices as discussed for the minimax-lils-S tuning discussed later in this article. Well, we've established our <math>MS_{\text{p}}</math> equivalent, but we still need an equivalent for <math>S_{\text{p}}</math> alone. This is <math>L^{-1}</math>, but with an extra 1 before the logs of primes are diagonalized: | As for the minimax-E-lils-S tuning, we use the pseudoinverse method, but with the same augmented matrices as discussed for the minimax-lils-S tuning discussed later in this article. Well, we've established our <math>MS_{\text{p}}</math> equivalent, but we still need an equivalent for <math>S_{\text{p}}</math> alone. This is <math>L^{-1}</math>, but with an extra 1 before the logs of primes are diagonalized: | ||
Line 2,875: | Line 2,833: | ||
This too can be computed easily with the Wolfram Library: | This too can be computed easily with the Wolfram Library: | ||
<pre> | |||
In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-ES"] | In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-ES"] | ||
Out: {1199.544 163.888] </ | Out: {1199.544 163.888] </pre> | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT | === Minimax-E-lols-S === | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Alternative complexities#Log-odd-limit-squared2]]. We use the pseudoinverse method, with our same <math>MS_{\text{p}}</math> and <math>S_{\text{p}}</math> equivalents as from the minimax-E-lils-S examples: | |||
Line 3,095: | Line 3,052: | ||
This too can be computed by the Wolfram Library: | This too can be computed by the Wolfram Library: | ||
<pre> | |||
In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "held-octave minimax-E-lils-S"] | In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "held-octave minimax-E-lils-S"] | ||
Out: {1200 164.062] </ | Out: {1200 164.062] </pre> | ||
= Zero-damage method = | |||
The second optimization power we'll take a look at is <math>p = 1</math>, for miniaverage tuning schemes. | The second optimization power we'll take a look at is <math>p = 1</math>, for miniaverage tuning schemes. | ||
Note that miniaverage tunings have not been advocated by tuning theorists thus far. We've included this section largely in order to complete the set of methods with exact solutions, one for each of the key optimization powers <math>1</math>, <math>2</math>, and <math>∞</math>.<ref>Another reason we wrote the method for this optimization power up is because it was low-hanging fruit, on account of the fact that it was already described on the wiki in the [[Target tunings]] page, where it is presented as a method for finding "minimax" tunings, not ''miniaverage'' tunings. This is somewhat misleading, because while this method works for any miniaverage tuning scheme, it only works for some minimax tuning schemes under very specific conditions (which that page does meet, and so it's not outright ''wrong''). The conditions are: unity-weight damage (check), and all members of the target-interval set expressible as products of other members (check, due to their choice of target-interval set, closely related to a tonality diamond, plus octaves are constrained to be unchanged). The reason why these are the two necessary conditions for this miniaverage method working for a minimax tuning scheme is because when you are solving for the minimax, you actually want the tunings where the target-intervals' damages ''equal each other'', not where they are ''zero'', and these zero-damage tunings will only match the tunings where other intervals' damages equal each other in the case where two intervals' damages being equal implies that another target's damage is zero, because that other target is the product of those first two; and the unity-weight damage requirement is to ensure that the slopes of each target's [[ | Note that miniaverage tunings have not been advocated by tuning theorists thus far. We've included this section largely in order to complete the set of methods with exact solutions, one for each of the key optimization powers <math>1</math>, <math>2</math>, and <math>∞</math>.<ref group="note">Another reason we wrote the method for this optimization power up is because it was low-hanging fruit, on account of the fact that it was already described on the wiki in the [[Target tunings]] page, where it is presented as a method for finding "minimax" tunings, not ''miniaverage'' tunings. This is somewhat misleading, because while this method works for any miniaverage tuning scheme, it only works for some minimax tuning schemes under very specific conditions (which that page does meet, and so it's not outright ''wrong''). The conditions are: unity-weight damage (check), and all members of the target-interval set expressible as products of other members (check, due to their choice of target-interval set, closely related to a tonality diamond, plus octaves are constrained to be unchanged). The reason why these are the two necessary conditions for this miniaverage method working for a minimax tuning scheme is because when you are solving for the minimax, you actually want the tunings where the target-intervals' damages ''equal each other'', not where they are ''zero'', and these zero-damage tunings will only match the tunings where other intervals' damages equal each other in the case where two intervals' damages being equal implies that another target's damage is zero, because that other target is the product of those first two; and the unity-weight damage requirement is to ensure that the slopes of each target's [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning fundamentals#3D tuning damage graphs|hyper-V]] are all the same, because otherwise the points where two damages are equal like this will no longer line up directly over the point where the third target's damage is zero. For more information on this problem, please see the discussion page for the problematic wiki page, where we are currently requesting the page be updated accordingly.</ref> So, you may prefer to skip ahead to the next section if you're feeling more practically minded. However, the method for <math>p = ∞</math> is related but more complicated, and its explanation builds upon this method's explanation, so it may still be worth it to work through this one first. | ||
The high-level summary here is that we're going to collect every tuning where one target-interval for each generator is tuned pure simultaneously. Then we will check each of those tunings' damages, and choose the tuning of those which causes the least damage. | The high-level summary here is that we're going to collect every tuning where one target-interval for each generator is tuned pure simultaneously. Then we will check each of those tunings' damages, and choose the tuning of those which causes the least damage. | ||
==The zero-damage point set== | == The zero-damage point set == | ||
The method for finding the miniaverage leverages the fact that the sum graph changes slope wherever a target-interval is tuned pure. The minimum must be found among the points where <math>r</math> target-intervals are all tuned pure at once, where <math>r</math> is the rank of the temperament. This is because this is the maximum number of linearly independent intervals that could be pure at once, given only <math>r</math> generators to work with. You can imagine that for any point you could find where only <math>r - 1</math> intervals were pure at once, that point would be found on a line along which all <math>r - 1</math> of those intervals remain pure, but if you follow it far enough in one direction, you'll reach a point where one additional interval is also pure. | The method for finding the miniaverage leverages the fact that the sum graph changes slope wherever a target-interval is tuned pure. The minimum must be found among the points where <math>r</math> target-intervals are all tuned pure at once, where <math>r</math> is the rank of the temperament. This is because this is the maximum number of linearly independent intervals that could be pure at once, given only <math>r</math> generators to work with. You can imagine that for any point you could find where only <math>r - 1</math> intervals were pure at once, that point would be found on a line along which all <math>r - 1</math> of those intervals remain pure, but if you follow it far enough in one direction, you'll reach a point where one additional interval is also pure. | ||
Line 3,115: | Line 3,070: | ||
So, in essence, this method works by narrowing the infinite space of tuning possibilities down to a finite set of points to check. We gather these zero-damage points, find the damage (specifically the ''sum'' of damages to the target-intervals, AKA the power sum where <math>p = 1</math>) at each point, and then choose the one with the minimum damage out of those. And that'll be our miniaverage tuning (unless there's a tie, but more on that later). | So, in essence, this method works by narrowing the infinite space of tuning possibilities down to a finite set of points to check. We gather these zero-damage points, find the damage (specifically the ''sum'' of damages to the target-intervals, AKA the power sum where <math>p = 1</math>) at each point, and then choose the one with the minimum damage out of those. And that'll be our miniaverage tuning (unless there's a tie, but more on that later). | ||
==Gather and process zero-damage points== | == Gather and process zero-damage points == | ||
Let's practice this method by working through an example. For our target-interval list, we can use our recommended scheme, the [[TILT|truncated integer limit triangle]] (or "TILT" for short), colorized here so we'll be able to visualize their combinations better in the upcoming step. This is the 6-TILT, our default target list for 5-limit temperaments. | Let's practice this method by working through an example. For our target-interval list, we can use our recommended scheme, the [[TILT|truncated integer limit triangle]] (or "TILT" for short), colorized here so we'll be able to visualize their combinations better in the upcoming step. This is the 6-TILT, our default target list for 5-limit temperaments. | ||
Line 3,143: | Line 3,097: | ||
And let's use a classic example for our temperament: meantone. | And let's use a classic example for our temperament: meantone. | ||
===Unchanged-interval bases=== | === Unchanged-interval bases === | ||
We can compute ahead of time how many points we should find in our zero-damage point set, because it's simply the number of combinations of <math>r</math> of them. With meantone being a rank-2 temperament, that's <math>{{8}\choose{2}} = 28</math> points (8 choose 2 is 28). | We can compute ahead of time how many points we should find in our zero-damage point set, because it's simply the number of combinations of <math>r</math> of them. With meantone being a rank-2 temperament, that's <math>{{8}\choose{2}} = 28</math> points (8 choose 2 is 28). | ||
Line 3,609: | Line 3,562: | ||
===Canonicalize and filter deficient matrices=== | === Canonicalize and filter deficient matrices === | ||
But many of these unchanged-interval bases are actually redundant with each other, by which we mean that they correspond to the same tuning. Said another way, some of these unchanged-interval bases are different bases for the same set of unchanged-intervals. | But many of these unchanged-interval bases are actually redundant with each other, by which we mean that they correspond to the same tuning. Said another way, some of these unchanged-interval bases are different bases for the same set of unchanged-intervals. | ||
Line 4,018: | Line 3,970: | ||
In some cases at this point, we would eliminate some unchanged-interval bases, those that through the process of canonicalization were simplified to fewer than <math>r</math> intervals, i.e. they lost a column (or more than one column). In this example, that has not occurred to any of our matrices; in order for it to have occurred, our target-interval set would have needed to include [[linearly dependent]] intervals. For example, the intervals <math>\frac32</math> and <math>\frac94</math> are linearly dependent, and we see these in the 10-TILT that's the default for a 7-limit temperament. So in that case, the unchanged-interval bases that result from the combination of those pairs of intervals will be eliminated. This captures the fact that if you were to purely tune the interval which the others are multiples of, all the others will also be purely tuned, so this is not truly a combination of distinct intervals to purely tune. | In some cases at this point, we would eliminate some unchanged-interval bases, those that through the process of canonicalization were simplified to fewer than <math>r</math> intervals, i.e. they lost a column (or more than one column). In this example, that has not occurred to any of our matrices; in order for it to have occurred, our target-interval set would have needed to include [[linearly dependent]] intervals. For example, the intervals <math>\frac32</math> and <math>\frac94</math> are linearly dependent, and we see these in the 10-TILT that's the default for a 7-limit temperament. So in that case, the unchanged-interval bases that result from the combination of those pairs of intervals will be eliminated. This captures the fact that if you were to purely tune the interval which the others are multiples of, all the others will also be purely tuned, so this is not truly a combination of distinct intervals to purely tune. | ||
===De-dupe=== | === De-dupe === | ||
And we also see that our <math>\frac32</math> and <math>\frac65</math> matrix has been changed to <math>\frac32</math> and <math>\frac54</math>. This may be less obvious in terms of it being a simplification, but it does illuminate how tuning <math>\frac32</math> and <math>\frac65</math> pure is no different than tuning <math>\frac32</math> and <math>\frac54</math> pure. | And we also see that our <math>\frac32</math> and <math>\frac65</math> matrix has been changed to <math>\frac32</math> and <math>\frac54</math>. This may be less obvious in terms of it being a simplification, but it does illuminate how tuning <math>\frac32</math> and <math>\frac65</math> pure is no different than tuning <math>\frac32</math> and <math>\frac54</math> pure. | ||
Line 4,185: | Line 4,136: | ||
Counting only 11 matrices still remaining, that means we must have eliminated 17 of them as redundant from our original set of 28. | Counting only 11 matrices still remaining, that means we must have eliminated 17 of them as redundant from our original set of 28. | ||
===Convert to generators=== | === Convert to generators === | ||
Now we just need to convert each of these unchanged-interval bases <math>\mathrm{U}_{(i,j)}</math> to a corresponding generator embedding <math>G</math>. To do this, we use the formula <math>G = \mathrm{U}(M\mathrm{U})^{-1}</math>, where <math>M</math> is the temperament mapping (the derivation of this formula, and examples of working through this calculation, are both described later in this article here: [[#Only unchanged-intervals method]]).<ref group="note">Note that this technique for converting zero-damage points to generator tunings is much simpler than the technique described on the [[Target tunings]] page. The Target tunings page uses eigendecomposition, which unnecessarily requires you to find the commas for the temperament, compute a full [[projection matrix]] <math>P</math>, and then when you need to spit a generator tuning map <math>𝒈</math> out at the end, requires the computation of a [[generator detempering]] to do so (moreover, it doesn't explain or even mention eigendecomposition; it assumes the reader knows how and when to do them, cutting off at the point of listing the eigenvectors—a big thanks to Sintel for unpacking the thought process in that article for us). The technique described here skips the commas, computing the generator embedding <math>G</math> directly rather than via <math>P = GM</math>, and then when you need to spit a generator tuning map out at the end, it's just <math>𝒈 = 𝒋G</math>, which is much simpler than the generator detempering computation. | |||
Now we just need to convert each of these unchanged-interval bases <math>\mathrm{U}_{(i,j)}</math> to a corresponding generator embedding <math>G</math>. To do this, we use the formula <math>G = \mathrm{U}(M\mathrm{U})^{-1}</math>, where <math>M</math> is the temperament mapping (the derivation of this formula, and examples of working through this calculation, are both described later in this article here: [[#Only unchanged-intervals method]]).<ref>Note that this technique for converting zero-damage points to generator tunings is much simpler than the technique described on the [[Target tunings]] page. The Target tunings page uses eigendecomposition, which unnecessarily requires you to find the commas for the temperament, compute a full [[projection matrix]] <math>P</math>, and then when you need to spit a generator tuning map <math>𝒈</math> out at the end, requires the computation of a [[generator | |||
<br><br> | <br><br> | ||
The Target tunings approach and this approach are quite similar conceptually. Here's the Target tunings approach: | The Target tunings approach and this approach are quite similar conceptually. Here's the Target tunings approach: | ||
Line 4,284: | Line 4,234: | ||
</math> | </math> | ||
<br><br> | <br><br> | ||
So in the Target tunings approach, <math>P</math> is the projection matrix, <math>\mathrm{V}</math> is a matrix consisting of a list of unrotated | So in the Target tunings approach, <math>P</math> is the projection matrix, <math>\mathrm{V}</math> is a matrix consisting of a list of unrotated vectors—both ones with scaling factor 1 (unchanged-intervals) and those with scaling factor 0 (commas)—and <math>\textit{Λ}</math> is a diagonal scaling factors matrix, where you can see along the main diagonal we have 1's paired with the unrotated vectors for unchanged-intervals and 0's paired with the unrotated vectors for commas. | ||
<br><br> | <br><br> | ||
In our approach, we instead solve for <math>G</math> by leaving the commas out of the equation, and simply using the mapping <math>M</math> instead. | In our approach, we instead solve for <math>G</math> by leaving the commas out of the equation, and simply using the mapping <math>M</math> instead. | ||
Line 4,449: | Line 4,399: | ||
Note that every one of those unusual looking values | Note that every one of those unusual looking values above—whether it be <math>\frac21</math>, <math>\frac{81}{40}</math>, <math>\frac{8\sqrt[2]{5}}{9}</math>, or otherwise in the first column—or <math>\frac32</math>, <math>\frac{40}{27}</math>, <math>\sqrt[3]{\frac{10}{3}}</math>, or otherwise in the second column—is an approximation of <math>\frac21</math> or <math>\frac32</math>, respectively. | ||
At this point, the only inputs affecting our results have been <math>M</math> and <math>\mathrm{T}</math>: <math>M</math> appears in our formula for <math>G</math>, and our target-interval set <math>\mathrm{T}</math> was our source of intervals for our set of unchanged-interval bases. Notably <math>W</math> is missing from that list of inputs affecting our results. So at this point, it doesn't seem to matter what our damage weight slope is (or what the complexity function used for it is, if other than log-product complexity); this list of candidate <math>G</math>'s is valid in any case of <math>W</math>. But don't worry; <math>W</math> will definitely affect the results soon; actually, it comes into play in the next step. | At this point, the only inputs affecting our results have been <math>M</math> and <math>\mathrm{T}</math>: <math>M</math> appears in our formula for <math>G</math>, and our target-interval set <math>\mathrm{T}</math> was our source of intervals for our set of unchanged-interval bases. Notably <math>W</math> is missing from that list of inputs affecting our results. So at this point, it doesn't seem to matter what our damage weight slope is (or what the complexity function used for it is, if other than log-product complexity); this list of candidate <math>G</math>'s is valid in any case of <math>W</math>. But don't worry; <math>W</math> will definitely affect the results soon; actually, it comes into play in the next step. | ||
==Find damages at points== | == Find damages at points == | ||
As the next step, we find the <math>1</math>-sum of the damages to the target-interval set for each of those tunings. We'll work through one example. Let's just grab that third <math>G</math>, then, the one with <math>\frac21</math> and <math>\sqrt[3]{\frac{10}{3}}</math>. | As the next step, we find the <math>1</math>-sum of the damages to the target-interval set for each of those tunings. We'll work through one example. Let's just grab that third <math>G</math>, then, the one with <math>\frac21</math> and <math>\sqrt[3]{\frac{10}{3}}</math>. | ||
Line 4,466: | Line 4,415: | ||
As discussed in [[Dave Keenan & Douglas Blumeyer's guide to RTT | As discussed in [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning fundamentals#Absolute errors]], these vertical bars mean to take the absolute value of each entry of this vector, not to take its magnitude. | ||
As discussed elsewhere, we can simplify this to: | As discussed elsewhere, we can simplify this to: | ||
Line 4,476: | Line 4,425: | ||
So here's that. Since we've gone with [[ | So here's that. Since we've gone with [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning fundamentals#Weight_slope|simplicity-weight damage]] here, we'll be using <math>S</math> to represent our simplicity-weight matrix rather than the generic <math>W</math> for weight matrix: | ||
Line 4,818: | Line 4,767: | ||
And finally since this tuning scheme is all about the sum of damages, we're actually looking for <math> \llzigzag \textbf{d} \rrzigzag _1</math>. So we total these up, and get our final answer: 0.000 + 4.523 + 2.773 + 2.000 + 2.158 + 0.000 + 1.659 + 0.000 = 13.114. And that's in units of simplicity-weighted cents, ¢(S), by the way. | And finally since this tuning scheme is all about the sum of damages, we're actually looking for <math> \llzigzag \textbf{d} \rrzigzag _1</math>. So we total these up, and get our final answer: 0.000 + 4.523 + 2.773 + 2.000 + 2.158 + 0.000 + 1.659 + 0.000 = 13.114. And that's in units of simplicity-weighted cents, ¢(S), by the way. | ||
==Choose the winner== | == Choose the winner == | ||
Now, if we repeat that entire damage calculation process for every one of the eleven tunings we identified as candidates for the miniaverage, then we'd have found the following list of tuning damages: 21.338, 9.444, 13.114, 10.461, 15.658, 10.615, 50.433, 26.527, 25.404, 33.910, and 80.393. So 13.114 isn't bad, but it's apparently not the best we can do. That honor goes to the ''second'' tuning there, which has only 9.444 ¢(S) total damage. | Now, if we repeat that entire damage calculation process for every one of the eleven tunings we identified as candidates for the miniaverage, then we'd have found the following list of tuning damages: 21.338, 9.444, 13.114, 10.461, 15.658, 10.615, 50.433, 26.527, 25.404, 33.910, and 80.393. So 13.114 isn't bad, but it's apparently not the best we can do. That honor goes to the ''second'' tuning there, which has only 9.444 ¢(S) total damage. | ||
Line 4,834: | Line 4,782: | ||
Often people will prefer to have the tuning in terms of the cents sizes of the generators, which is our generator tuning map <math>𝒈</math><ref>Technically this gives us the ''tunings'' of the generators, in ¢/g.</ref>, but again we can find that as easily as <math>𝒋G</math>: | Often people will prefer to have the tuning in terms of the cents sizes of the generators, which is our generator tuning map <math>𝒈</math><ref group="note">Technically this gives us the ''tunings'' of the generators, in ¢/g.</ref>, but again we can find that as easily as <math>𝒋G</math>: | ||
Line 4,860: | Line 4,808: | ||
And that works out to {{map|1200.000 696.578}}. | And that works out to {{map|1200.000 696.578}}. | ||
==Tie-breaking== | == Tie-breaking == | ||
With the 6-TILT miniaverage tuning of meantone (with simplicity-weight damage), we've solved for a unique tuning based on <math>G</math> that miniaverages the damage to this temperament <math>M</math>. | With the 6-TILT miniaverage tuning of meantone (with simplicity-weight damage), we've solved for a unique tuning based on <math>G</math> that miniaverages the damage to this temperament <math>M</math>. | ||
But sometimes we have a tie between tunings for least average damage, though. For example, if we had we done a unity-weight tuning, in which case <math>W = I</math>, and included the interval <math>\frac85</math> in our set, we would have found that quarter-comma meantone tied with another tuning, one with generators of <math>\sqrt[5]{\frac{2560}{81}}</math> and <math>\sqrt[5]{\frac{200}{27}}</math>, which are approximately 1195.7 ¢ and 693.352 ¢. | But sometimes we have a tie between tunings for least average damage, though. For example, if we had we done a unity-weight tuning, in which case <math>W = I</math>, and included the interval <math>\frac85</math> in our set, we would have found that quarter-comma meantone tied with another tuning, one with generators of <math>\sqrt[5]{\frac{2560}{81}}</math> and <math>\sqrt[5]{\frac{200}{27}}</math>, which are approximately 1195.7 ¢ and 693.352 ¢. | ||
In this case, we fall back to our general method, which is equipped to find the true optimum somewhere in between these two extreme ends of goodness, albeit as an approximate solution.<ref>The article for the minimax tuning scheme, [[Target tunings]], suggests that you fall back to the miniRMS method to tie-break between these, but that sort of misses the point of the problem. The two tied points are on extreme opposite ends of the slice of good solutions, and the optimum solution lies somewhere in between them. We don't want the tie-break to choose one or the other extreme; we want to find a better solution somewhere in between them.</ref> This method is discussed here: [[ | In this case, we fall back to our general method, which is equipped to find the true optimum somewhere in between these two extreme ends of goodness, albeit as an approximate solution.<ref group="note">The article for the minimax tuning scheme, [[Target tunings]], suggests that you fall back to the miniRMS method to tie-break between these, but that sort of misses the point of the problem. The two tied points are on extreme opposite ends of the slice of good solutions, and the optimum solution lies somewhere in between them. We don't want the tie-break to choose one or the other extreme; we want to find a better solution somewhere in between them.</ref> This method is discussed here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Tie_breaking:_power_limit_method|power limit method]]. Or, if you'd like a refresher on how to think about non-unique tunings, please see [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning fundamentals#Non-unique tunings]]. | ||
== | We note that there may be a way to find an exact solution to a nested miniaverage, in a similar fashion to the nested minimax discussed in the coinciding-damage method section below, but it raises some conceptual issues about what a nested miniaverage even means.<ref group="note">There does not seem to be any consensus about how to identify a true optimum in the case of multiple solutions when <math>p=1</math>. See https://en.wikipedia.org/wiki/Least_absolute_deviations#Properties, https://www.researchgate.net/publication/223752233_Dealing_with_the_multiplicity_of_solutions_of_the_l1_and_l_regression_models, and https://stats.stackexchange.com/questions/275931/is-it-possible-to-force-least-absolute-deviations-lad-regression-to-return-the.</ref> We have done some pondering of this problem but it remains open; we didn't prioritize solving it, on account of the fact that nobody uses miniaverage tunings anyway. | ||
The zero-damage method is easily modified to handle held-intervals along with target-intervals.<ref>In fact, the Target tunings page of the wiki uses this more complicated approach in order to realize pure octaves, and so the authors of this page had to reverse engineer from it how to make it work ''without'' any held-intervals.</ref> In short, rather than assembling our set of unchanged-interval bases <math>\mathrm{U}_1</math> through <math>\mathrm{U}_n</math> (where <math>n = {{k}\choose{r}}</math>) corresponding to the zero-damage points by finding every combination of <math>r</math> different ones of our <math>k</math> target-intervals (one for each generator to be responsible for tuning exactly), instead we must first reserve <math>h</math> (''held''-unchanged-interval count) columns of each <math>\mathrm{U}_n</math> for the held-intervals, leaving only the remaining <math>r - h</math> columns to be assembled from the target-intervals as normal. So, we'll only have <math>{{k}\choose{r - h}}</math> candidate tunings / zero-damage points / unchanged-interval bases in this case. | == With held-intervals == | ||
The zero-damage method is easily modified to handle held-intervals along with target-intervals.<ref group="note">In fact, the Target tunings page of the wiki uses this more complicated approach in order to realize pure octaves, and so the authors of this page had to reverse engineer from it how to make it work ''without'' any held-intervals.</ref> In short, rather than assembling our set of unchanged-interval bases <math>\mathrm{U}_1</math> through <math>\mathrm{U}_n</math> (where <math>n = {{k}\choose{r}}</math>) corresponding to the zero-damage points by finding every combination of <math>r</math> different ones of our <math>k</math> target-intervals (one for each generator to be responsible for tuning exactly), instead we must first reserve <math>h</math> (''held''-unchanged-interval count) columns of each <math>\mathrm{U}_n</math> for the held-intervals, leaving only the remaining <math>r - h</math> columns to be assembled from the target-intervals as normal. So, we'll only have <math>{{k}\choose{r - h}}</math> candidate tunings / zero-damage points / unchanged-interval bases in this case. | |||
In other words, if <math>\mathrm{U}_n</math> is one of the unchanged-interval bases characterizing a candidate miniaverage tuning, then it must contain <math>\mathrm{H}</math> itself, the held-interval basis, which does not yet fully characterize our tuning, leaving some wiggle room (otherwise we'd just use the "[[#Only held-intervals method|only held-intervals]]" approach, discussed later). | In other words, if <math>\mathrm{U}_n</math> is one of the unchanged-interval bases characterizing a candidate miniaverage tuning, then it must contain <math>\mathrm{H}</math> itself, the held-interval basis, which does not yet fully characterize our tuning, leaving some wiggle room (otherwise we'd just use the "[[#Only held-intervals method|only held-intervals]]" approach, discussed later). | ||
Line 5,025: | Line 4,971: | ||
(Note that <math>\mathrm{U}_{(1)}</math> here pairs <math>\frac21</math> with <math>\frac21</math>. That's because the octave happens to appear both in our held-interval basis <math>\mathrm{H}</math> ''and'' our target-interval list <math>\mathrm{T}</math>. We could have chosen to remove <math>\frac21</math> from <math>\mathrm{T}</math> upon adding it to <math>\mathrm{H}</math>, because once you're insisting a particular interval takes no damage there's no sense also including it in a list of intervals to minimize damage to. But we chose to leave <math>\mathrm{T}</math> alone to make our points above more clearly, i.e. with <math>k</math> remaining equal to <math>8</math>.)<ref>Held-intervals should generally be removed if they also appear in the target-interval list <math>\mathrm{T}</math>. 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. In general, it should be more computationally efficient to remove these intervals from <math>\mathrm{T}</math> 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.</ref> | (Note that <math>\mathrm{U}_{(1)}</math> here pairs <math>\frac21</math> with <math>\frac21</math>. That's because the octave happens to appear both in our held-interval basis <math>\mathrm{H}</math> ''and'' our target-interval list <math>\mathrm{T}</math>. We could have chosen to remove <math>\frac21</math> from <math>\mathrm{T}</math> upon adding it to <math>\mathrm{H}</math>, because once you're insisting a particular interval takes no damage there's no sense also including it in a list of intervals to minimize damage to. But we chose to leave <math>\mathrm{T}</math> alone to make our points above more clearly, i.e. with <math>k</math> remaining equal to <math>8</math>.)<ref group="note">Held-intervals should generally be removed if they also appear in the target-interval list <math>\mathrm{T}</math>. 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. In general, it should be more computationally efficient to remove these intervals from <math>\mathrm{T}</math> 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.</ref> | ||
Now we canonicalize (no need for color anymore; the point has been made about the combinations of target-intervals with held-intervals): | Now we canonicalize (no need for color anymore; the point has been made about the combinations of target-intervals with held-intervals): | ||
Line 5,243: | Line 5,189: | ||
And convert those to generator tuning maps: {{map|1200 701.955}}, {{map|1200 696.578}}, and {{map|1200 694.786}}. Note that every one of these has a pure-octave period. Then check the damage sums: 353.942 ¢(U), 89.083 ¢(U), and 110.390 ¢(U), respectively. So that tells us that we want the middle result of these three, {{map|1200 696.578}}, as the minimization of the <math>1</math>-mean of unity-weight damage to the 6-TILT, when we're constrained to the octave being unchanged. | And convert those to generator tuning maps: {{map|1200 701.955}}, {{map|1200 696.578}}, and {{map|1200 694.786}}. Note that every one of these has a pure-octave period. Then check the damage sums: 353.942 ¢(U), 89.083 ¢(U), and 110.390 ¢(U), respectively. So that tells us that we want the middle result of these three, {{map|1200 696.578}}, as the minimization of the <math>1</math>-mean of unity-weight damage to the 6-TILT, when we're constrained to the octave being unchanged. | ||
For a rank-3 temperament, with 2 held-intervals, we'd again have 8 choose 1 = 8 tunings to check. With 1 held-interval, we'd have 8 choose 2 = 28 tunings to check. | For a rank-3 temperament, with 2 held-intervals, we'd again have 8 choose 1 = 8 tunings to check. With 1 held-interval, we'd have 8 choose 2 = 28 tunings to check. | ||
== For all-interval tuning schemes == | |||
We can adapt the zero-damage method to compute all-interval tuning schemes where the dual norm power <math>\text{dual}(q)</math> is equal to <math>1</math>.. | We can adapt the zero-damage method to compute all-interval tuning schemes where the dual norm power <math>\text{dual}(q)</math> is equal to <math>1</math>.. | ||
===Maxization=== | === Maxization === | ||
Per the heading of this section, we might call these "minimax-MS" schemes, where the 'M' here indicates that their interval complexity functions have been "maxized", as opposed to "Euclideanized"; that is, the power and matching root from their norm or summation form has been changed to <math>∞</math> instead of to <math>2</math>. "Maxization" can be thought of as a reference to the fact that distance measured by <math>∞</math>-norms (maxes, remember) resembles distance traveled by "Max the magician" to get from point A to point B; he can teleport through all dimensions except the one he needs to travel furthest in, i.e. the maximum distance he had to go in any one dimension, is the defining distance. (To complete the set, the <math>1</math>-norms could be referred to as "taxicabized", referencing that this is the type of distance a taxicab on a grid of streets would travel… though would these tunings really be "-ized" if this is the logical starting point?) | Per the heading of this section, we might call these "minimax-MS" schemes, where the 'M' here indicates that their interval complexity functions have been "maxized", as opposed to "Euclideanized"; that is, the power and matching root from their norm or summation form has been changed to <math>∞</math> instead of to <math>2</math>. "Maxization" can be thought of as a reference to the fact that distance measured by <math>∞</math>-norms (maxes, remember) resembles distance traveled by "Max the magician" to get from point A to point B; he can teleport through all dimensions except the one he needs to travel furthest in, i.e. the maximum distance he had to go in any one dimension, is the defining distance. (To complete the set, the <math>1</math>-norms could be referred to as "taxicabized", referencing that this is the type of distance a taxicab on a grid of streets would travel… though would these tunings really be "-ized" if this is the logical starting point?) | ||
And to be clear, the <math>\textbf{i}</math>-norm is maxized | And to be clear, the <math>\textbf{i}</math>-norm is maxized here—has norm power <math>∞</math>—because the norm power on the retuning magnitude is <math>1</math>, and these norm powers must be duals. | ||
Tuning schemes such as these are not very popular, because where Euclideanizing <math>\text{lp-C}()</math> already makes tunings less psychoacoustically plausible, maxizing it makes tunings even less plausible. | Tuning schemes such as these are not very popular, because where Euclideanizing <math>\text{lp-C}()</math> already makes tunings less psychoacoustically plausible, maxizing it makes tunings even less plausible. | ||
===Example=== | === Example === | ||
Let's compute the minimax-MS tuning of meantone temperament. We begin by assembling our list of unchanged-interval bases. This list will be much shorter than it was with ordinary tuning schemes, because the size of this list increases combinatorially with the count of target-intervals, and with only three (proxy) target-intervals here for a 5-limit temperament. | Let's compute the minimax-MS tuning of meantone temperament. We begin by assembling our list of unchanged-interval bases. This list will be much shorter than it was with ordinary tuning schemes, because the size of this list increases combinatorially with the count of target-intervals, and with only three (proxy) target-intervals here for a 5-limit temperament. | ||
Line 5,369: | Line 5,312: | ||
And so quarter-comma tuning is our winner with the least retuning magnitude. That's the minimax-MS tuning of meantone. | And so quarter-comma tuning is our winner with the least retuning magnitude. That's the minimax-MS tuning of meantone. | ||
==With alternative complexities== | == With alternative complexities == | ||
No examples will be given here, on account of the lack of popularity of these tunings. | No examples will be given here, on account of the lack of popularity of these tunings. | ||
=Coinciding-damage method= | = Coinciding-damage method = | ||
The third and final specific optimization power we'll take a look at in this article is <math>p = ∞</math>, for minimax tuning schemes. | The third and final specific optimization power we'll take a look at in this article is <math>p = ∞</math>, for minimax tuning schemes. | ||
Line 5,381: | Line 5,322: | ||
# Where the <math>p=1</math> method is not capable of tie-breaking when the basic mini-<math>p</math>-mean is a range of tunings rather than a single unique optimum tuning, this <math>p=∞</math> method ''is'' capable of tie-breaking, to find the true single unique optimum tuning. | # Where the <math>p=1</math> method is not capable of tie-breaking when the basic mini-<math>p</math>-mean is a range of tunings rather than a single unique optimum tuning, this <math>p=∞</math> method ''is'' capable of tie-breaking, to find the true single unique optimum tuning. | ||
==History== | == History == | ||
This method was originally developed by [[Keenan Pepper]] in 2012,<ref group="note">See https://yahootuninggroupsultimatebackup.github.io/tuning-math/topicId_20405.html and https://yahootuninggroupsultimatebackup.github.io/tuning-math/topicId_21022.html</ref>, in a 142-line long [https://www.python.org/ Python] file called [https://github.com/keenanpepper/tiptop/blob/main/tiptop.py tiptop.py]. | |||
This method was originally developed by [[Keenan Pepper]] in 2012,<ref>See https://yahootuninggroupsultimatebackup.github.io/tuning-math/topicId_20405.html and https://yahootuninggroupsultimatebackup.github.io/tuning-math/topicId_21022.html</ref>, in a 142-line long [ | |||
Keenan developed his algorithm specifically for the minimax-S tuning scheme (historically known as "TOP"), the original and quintessential all-interval tuning scheme. The all-interval use case is discussed below in the "For all-interval tuning schemes" section. | Keenan developed his algorithm specifically for the minimax-S tuning scheme (historically known as "TOP"), the original and quintessential all-interval tuning scheme. The all-interval use case is discussed below in the "For all-interval tuning schemes" section. | ||
Specifically, Keenan's method was developed for its tie-breaking abilities, at a time where the [[power-limit method]]'s ability to tie-break was unknown or not popular. | Specifically, Keenan's method was developed for its tie-breaking abilities, at a time where the [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Tie_breaking:_power_limit_method|power-limit method]]'s ability to tie-break was unknown or not popular. | ||
Keenan's method was modified in 2021-2023 by [[Douglas Blumeyer]] in order to accommodate ordinary | Keenan's method was modified in 2021-2023 by [[Douglas Blumeyer]] in order to accommodate ordinary tunings—those with target-interval sets where the optimization power is <math>∞</math> and the norm power may be anything or possibly absent—and this is what will be discussed immediately below. Douglas's modifications also included support for held-intervals, and for alternative complexities, both of which are discussed in sections below, and also an improvement that both simplifies it conceptually and allows it to identify optimum tunings more quickly. [[Dave Keenan]] further modified Keenan's method during this time so that it can find exact solutions in the form of generator embeddings, which is also reflected in all the explanations below. | ||
The explanation of how this method works is mostly by Douglas Blumeyer, but Dave Keenan and Keenan Pepper himself both helped tremendously with refining it. (Douglas takes credit for any shortcomings, however. In particular, he apologizes: he didn’t have time to make it shorter.) | The explanation of how this method works is mostly by Douglas Blumeyer, but Dave Keenan and Keenan Pepper himself both helped tremendously with refining it. (Douglas takes credit for any shortcomings, however. In particular, he apologizes: he didn’t have time to make it shorter.) | ||
==Coinciding-damage points== | == Coinciding-damage points == | ||
=== Points for finding damage minimaxes === | |||
===Points for finding damage minimaxes=== | Damage ''minimaxes'' are always found at points in tuning damage space where individual target-interval [[Dave Keenan & Douglas Blumeyer's guide to RTT/tuning_fundamentals#3D_tuning_damage_graphs|hyper-V]] damage graphs intersect, or cross, to form a point. | ||
Damage ''minimaxes'' are always found at points in tuning damage space where individual target-interval [[ | |||
This doesn't mean that every such point will be a damage minimax. It only means that every damage minimax will be such a point. | This doesn't mean that every such point will be a damage minimax. It only means that every damage minimax will be such a point. | ||
Line 5,436: | Line 5,374: | ||
Now, it might seem inefficient to check 14 points, the ones that only meet the first bullet's criterion, just to find the one we want that meets all three bullet's criteria. But actually, for a computer, 14 points is easy peasy. If we compared that with how many points it would check while following the general method for solving this, that could be thousands of times more, and it still would only be finding an approximate solution; the general method has a weaker understanding of the nature of the problem it's solving. In fact, this diagram shows a very simple 3-limit temperament with only 4 target-intervals, and the typical case is going to be 7-limit or higher with a dozen or more target-intervals, which gets exponentially more complex. But even then it may still be fewer points for the computer to check overall, even though many of them are not going to work out. | Now, it might seem inefficient to check 14 points, the ones that only meet the first bullet's criterion, just to find the one we want that meets all three bullet's criteria. But actually, for a computer, 14 points is easy peasy. If we compared that with how many points it would check while following the general method for solving this, that could be thousands of times more, and it still would only be finding an approximate solution; the general method has a weaker understanding of the nature of the problem it's solving. In fact, this diagram shows a very simple 3-limit temperament with only 4 target-intervals, and the typical case is going to be 7-limit or higher with a dozen or more target-intervals, which gets exponentially more complex. But even then it may still be fewer points for the computer to check overall, even though many of them are not going to work out. | ||
And you might wonder: but why don't we just scan along the max graph and pick the <math>-+</math> point? Well, the problem is: we don't have a function for the max damage graph, other than defining it in terms of all the other target-interval damage graphs. So it turns out that checking all of these crossing points is a more efficient way for a computer to find this point, than doing it the way that might seem more obvious to a human observer. | And you might wonder: but why don't we just scan along the max graph and pick the <math>-+</math> point? Well, the problem is: we don't have a function for the max damage graph, other than defining it in terms of all the other target-interval damage graphs. So it turns out that checking all of these crossing points is a more efficient way for a computer to find this point, than doing it the way that might seem more obvious to a human observer.<pre>*</pre> Once we start learning about tie-breaking, we'll see that this is not always exactly the case. But it's fine for now. | ||
< | |||
=== Points for finding damage miniaverages === | |||
In our explanation of the zero-damage method for <math>p=1</math>, we saw a similar thing in action for damage ''miniaverages''. But these can only be found at a strict ''subset'' of such coinciding-damage points. Specifically, a damage miniaverage is found somewhere among the subset of coinciding-damage points wherever a sufficient count of individual target-intervals' hyper-V-shaped damage graphs intersect ''along the zero-damage floor of the world'', in other words, along their creases. | In our explanation of the zero-damage method for <math>p=1</math>, we saw a similar thing in action for damage ''miniaverages''. But these can only be found at a strict ''subset'' of such coinciding-damage points. Specifically, a damage miniaverage is found somewhere among the subset of coinciding-damage points wherever a sufficient count of individual target-intervals' hyper-V-shaped damage graphs intersect ''along the zero-damage floor of the world'', in other words, along their creases. | ||
Line 5,454: | Line 5,389: | ||
[[File:Average point floor.png|frameless|600x600px]] | [[File:Average point floor.png|frameless|600x600px]] | ||
===Zero-damage coincidings=== | === Zero-damage coincidings === | ||
So: both types of points are coinciding-damage points. In fact, it may be helpful for some readers to think of the zero-damage method and its zero-damage point set as the (coinciding-)zero-damage method and its (coinciding-)zero-damage point set. It simply uses only a specialized subset of coinciding-damage points. | So: both types of points are coinciding-damage points. In fact, it may be helpful for some readers to think of the zero-damage method and its zero-damage point set as the (coinciding-)zero-damage method and its (coinciding-)zero-damage point set. It simply uses only a specialized subset of coinciding-damage points. | ||
Line 5,466: | Line 5,400: | ||
But this also creates the terminological problem whereby in 2D, a single target-interval damage graph bouncing off the floor is wanted as a "coinciding-damage" point. In this case, we can reassure ourselves by imagining that the unison is always sort of in our target-interval set, and its graph is always the flat plane on the floor, since it can never be damaged. So in a way, the target-interval's damage coincides with the unison, and/or the unison is thought of as the "missing" interval, the one fewer that are required for an intersection here. | But this also creates the terminological problem whereby in 2D, a single target-interval damage graph bouncing off the floor is wanted as a "coinciding-damage" point. In this case, we can reassure ourselves by imagining that the unison is always sort of in our target-interval set, and its graph is always the flat plane on the floor, since it can never be damaged. So in a way, the target-interval's damage coincides with the unison, and/or the unison is thought of as the "missing" interval, the one fewer that are required for an intersection here. | ||
We may observe that the latter kind of point, the coinciding-''zero''-damage | We may observe that the latter kind of point, the coinciding-''zero''-damage points—those where damage graphs intersect on the zero-damage floor—may seem to be less likely candidates for minimax tunings, considering that by the nature of being on the zero-damage floor, where no target-interval's damage could possibly be any lower, there's almost certainly some target-interval with higher damage (whose damage is still increasing in one direction or another). And this can clearly be seen on the diagram we included a bit earlier. However, as we'll find in the later section about tie-breaking, and hinted at in an asterisked comment earlier, these points are often important for tie-breaking between tunings which are otherwise tied with each other when it comes to those higher-up damages (i.e. at least one direction, a higher-up damage is neither going up nor going down, such as along an intersection of two damage graphs whose creases are parallel). | ||
=== Generalizing to higher dimensions: Counts of target-intervals required to make the points === | |||
Another difference between the specific zero-damage points and general coinciding-damage points is that zero-damage points require one fewer target-interval damage graph to intersect in order to produce them. That's because a hyper-V's crease along the zero-damage floor has one fewer dimension than the hyper-V itself. | Another difference between the specific zero-damage points and general coinciding-damage points is that zero-damage points require one fewer target-interval damage graph to intersect in order to produce them. That's because a hyper-V's crease along the zero-damage floor has one fewer dimension than the hyper-V itself. | ||
Line 5,478: | Line 5,411: | ||
[[File:Cross to points.png|frameless|900x900px]] | [[File:Cross to points.png|frameless|900x900px]] | ||
In general, the number of target-intervals whose graphs will intersect at a | In general, the number of target-intervals whose graphs will intersect at a point—i.e., their damages will coincide—is equal to the dimension of the tuning damage space. So in 3D tuning damage, we need three hyper-V's to intersect. Think of it this way: a 3D point has a coordinate in the format <math>(x,y,z)</math>, and we need one plane, one target-interval, for each element of that coordinate. But for intersections among creases along the floor, we only need one ''less'' than the dimensionality of the tuning damage space to specify a point; that's because we already know that one of the coordinates is 0. | ||
The dimension of the tuning damage space will be equal to the count of generators plus one, or in other words, <math>r + 1</math>, where <math>r</math> is the [[rank]]. This is because tuning damage space has one dimension along the floor for each generator's tuning, and one additional dimension up off the floor for the damage amounts. | The dimension of the tuning damage space will be equal to the count of generators plus one, or in other words, <math>r + 1</math>, where <math>r</math> is the [[rank]]. This is because tuning damage space has one dimension along the floor for each generator's tuning, and one additional dimension up off the floor for the damage amounts. | ||
===Points vs. lines; tuning space vs. tuning damage space=== | === Points vs. lines; tuning space vs. tuning damage space === | ||
Throughout the discussion of this method, we may sometimes refer to "points" and "tunings" almost interchangeably. We'll attempt to dispel some potential confusion. | Throughout the discussion of this method, we may sometimes refer to "points" and "tunings" almost interchangeably. We'll attempt to dispel some potential confusion. | ||
In tuning damage space, a tuning corresponds with a vertical line, perpendicular to the zero damage floor. Any point on this line identifies this same tuning. If we took an aerial view on tuning damage space, looking straight down on | In tuning damage space, a tuning corresponds with a vertical line, perpendicular to the zero damage floor. Any point on this line identifies this same tuning. If we took an aerial view on tuning damage space, looking straight down on it—as we do in the [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning fundamentals#Topographic_tuning_damage_graphs|topographic, contour style graphs]] these lines ''would'' look like points. Basically in this view, the only dimensions are for the generators, and the extra dimension for damage is collapsed. In other words, we go from tuning damage space back to simply tuning space. | ||
So a 2D tuning damage space collapses to a 1D tuning space: a single line, a continuum of the single generator's size. And a 3D tuning damage space collapses to a 2D tuning space, with one generators' size per axis. | So a 2D tuning damage space collapses to a 1D tuning space: a single line, a continuum of the single generator's size. And a 3D tuning damage space collapses to a 2D tuning space, with one generators' size per axis. | ||
Line 5,494: | Line 5,426: | ||
It might seem wise to draw the vertical tuning lines that correspond with these points, but in general we've found that this is more noise than it's worth. | It might seem wise to draw the vertical tuning lines that correspond with these points, but in general we've found that this is more noise than it's worth. | ||
==How to gather coinciding-damage points== | == How to gather coinciding-damage points == | ||
=== For a general coinciding-damage point === | |||
===For a general coinciding-damage point=== | |||
The first step is to iterate over every combination of <math>r + 1</math> target-intervals, and for each of those combinations, look at all permutations of their relative [[Undirected_value#Direction|direction]]s. The rank <math>r</math> is the same as generator count in the basic case (later on we'll see how sometimes in this method it's different). And by "direction" we mean in the sense of "[[undirected value]]", i.e. are they greater than or less than unison. | The first step is to iterate over every combination of <math>r + 1</math> target-intervals, and for each of those combinations, look at all permutations of their relative [[Undirected_value#Direction|direction]]s. The rank <math>r</math> is the same as generator count in the basic case (later on we'll see how sometimes in this method it's different). And by "direction" we mean in the sense of "[[undirected value]]", i.e. are they greater than or less than unison. | ||
Line 5,514: | Line 5,444: | ||
Keenan came up with a clever way to achieve this only-caring-about-relative-direction permutations effect: simply restrict the first element in each combination to the positive direction. This effortlessly eliminates exactly half of the possible permutations, namely, the ones that are reciprocals of all the others. Done. | Keenan came up with a clever way to achieve this only-caring-about-relative-direction permutations effect: simply restrict the first element in each combination to the positive direction. This effortlessly eliminates exactly half of the possible permutations, namely, the ones that are reciprocals of all the others. Done. | ||
===For a zero-damage point=== | === For a zero-damage point === | ||
Gathering the zero-damage points is much more straightforward. We don't need to build a ReDPOTIC. We don't need to worry about ReD (relative direction), or permutations of (PO) anything. We only need to worry about TIC (target-interval combinations). | Gathering the zero-damage points is much more straightforward. We don't need to build a ReDPOTIC. We don't need to worry about ReD (relative direction), or permutations of (PO) anything. We only need to worry about TIC (target-interval combinations). | ||
Line 5,522: | Line 5,451: | ||
For each STIC, then, we simply want each combination of <math>r</math> of our target-intervals. For our example, with <math>r=1</math>, that'd simply be <math>\{ \{ \frac32 \}, \{ \frac54 \} , \{ \frac53 \} \}</math>. | For each STIC, then, we simply want each combination of <math>r</math> of our target-intervals. For our example, with <math>r=1</math>, that'd simply be <math>\{ \{ \frac32 \}, \{ \frac54 \} , \{ \frac53 \} \}</math>. | ||
==How to build constraint matrices== | == How to build constraint matrices == | ||
Once we've gathered all of our coinciding-damage points, both the general kind from ReDPOTICs and the zero-damage kind from STICs, we're ready to prepare '''constraint matrices'''. When we apply these to our familiar inequalities, we can convert them to solvable equalities. More on that in the next section, where we work through an example. | Once we've gathered all of our coinciding-damage points, both the general kind from ReDPOTICs and the zero-damage kind from STICs, we're ready to prepare '''constraint matrices'''. When we apply these to our familiar inequalities, we can convert them to solvable equalities. More on that in the next section, where we work through an example. | ||
Line 5,530: | Line 5,458: | ||
Let's call these matrices <math>K</math>, for "konstraint" ("C" is taken for a more widely important matrix in RTT, the [[comma basis]]). | Let's call these matrices <math>K</math>, for "konstraint" ("C" is taken for a more widely important matrix in RTT, the [[comma basis]]). | ||
===For a general coinciding-damage point=== | === For a general coinciding-damage point === | ||
With each of our general coinciding points, we can build a constraint matrix from its ReDPOTIC. Perhaps for some readers the approach could be best summed up near instantaneously by listing what these constraint matrices would be for the example we're going with so far: | With each of our general coinciding points, we can build a constraint matrix from its ReDPOTIC. Perhaps for some readers the approach could be best summed up near instantaneously by listing what these constraint matrices would be for the example we're going with so far: | ||
Line 5,591: | Line 5,518: | ||
Notice that each first nonzero entry in each constraint matrix is <math>+1</math>, per the previous section's point about effecting relative direction. | Notice that each first nonzero entry in each constraint matrix is <math>+1</math>, per the previous section's point about effecting relative direction. | ||
===For a zero-damage point=== | === For a zero-damage point === | ||
For each of our zero-damage points we can build a constraint matrix from its STIC. Here those are: | For each of our zero-damage points we can build a constraint matrix from its STIC. Here those are: | ||
Line 5,627: | Line 5,553: | ||
Note that since, as we noted earlier, relative direction is irrelevant for zero-damage points, these matrices will never contain -1 entries. They will only ever contain 0 and +1. | Note that since, as we noted earlier, relative direction is irrelevant for zero-damage points, these matrices will never contain -1 entries. They will only ever contain 0 and +1. | ||
==A simple example== | == A simple example == | ||
For our first overarching example, to help us intuit how this technique works, let's use a simplified example where our target-intervals are even simpler than the ones we looked at so far: just the primes, and thus <math>\mathrm{T} = \mathrm{T}_{\text{p}} = I</math>, an identity matrix we can ignore. | For our first overarching example, to help us intuit how this technique works, let's use a simplified example where our target-intervals are even simpler than the ones we looked at so far: just the primes, and thus <math>\mathrm{T} = \mathrm{T}_{\text{p}} = I</math>, an identity matrix we can ignore. | ||
Line 5,635: | Line 5,560: | ||
For our temperament, we'll go with the very familiar 12-ET, so <math>M</math> = {{map|12 19 28}}. Since this mapping is in the 5-prime-limit, our <math>𝒋</math> = 1200 × {{map|<math>\log_2(2)</math> <math>\log_2(3)</math> <math>\log_2(5)</math>}}. And since our mapping is an equal temperament, our generator tuning map <math>𝒈</math> has only a single entry <math>g_1</math>. | For our temperament, we'll go with the very familiar 12-ET, so <math>M</math> = {{map|12 19 28}}. Since this mapping is in the 5-prime-limit, our <math>𝒋</math> = 1200 × {{map|<math>\log_2(2)</math> <math>\log_2(3)</math> <math>\log_2(5)</math>}}. And since our mapping is an equal temperament, our generator tuning map <math>𝒈</math> has only a single entry <math>g_1</math>. | ||
===A system of approximations=== | === A system of approximations === | ||
We'll be applying a constraint matrix to our by-now familiar approximation <math>𝒈M \approx 𝒋</math> in order to transform it from an ''approximation'' into an ''equality'', that is, to be able to change its ''approximately equals'' sign into an ''equals'' sign. This is how each of these constraints take us to a single solution. | We'll be applying a constraint matrix to our by-now familiar approximation <math>𝒈M \approx 𝒋</math> in order to transform it from an ''approximation'' into an ''equality'', that is, to be able to change its ''approximately equals'' sign into an ''equals'' sign. This is how each of these constraints take us to a single solution. | ||
Line 5,668: | Line 5,592: | ||
Another way to view a matrix expression like this<ref>were everything transposed, anyway; a superficial issue</ref> is as a system of multiple | Another way to view a matrix expression like this<ref group="note">were everything transposed, anyway; a superficial issue</ref> is as a system of multiple expressions—in this case, a system of approximations: | ||
Line 5,692: | Line 5,616: | ||
Constraints we apply to the problem, however, ''can'' simplify it to a point where there is an actual solution, i.e. where the count of equations matches the count of variables, AKA the count of generators. | Constraints we apply to the problem, however, ''can'' simplify it to a point where there is an actual solution, i.e. where the count of equations matches the count of variables, AKA the count of generators. | ||
===Apply constraint to system=== | === Apply constraint to system === | ||
So let's try applying one of our constraint matrices to this equation. Suppose we get the constraint matrix [+1 +1 0]. (We may notice this happens to be one of those made from a ReDPOTIC, for a general coinciding-damage point.) This constraint matrix tells us that the target-interval combination is <math>\frac21</math> and <math>\frac31</math>, because those are the target-intervals corresponding to its nonzero entries. And both nonzero entries are <math>+1</math> meaning that both target-intervals are combined in the same direction. In other words, <math>\frac21 × \frac31 = \frac61</math> is going to have something to do with this (and we're finally about to find out what that is!). | So let's try applying one of our constraint matrices to this equation. Suppose we get the constraint matrix [+1 +1 0]. (We may notice this happens to be one of those made from a ReDPOTIC, for a general coinciding-damage point.) This constraint matrix tells us that the target-interval combination is <math>\frac21</math> and <math>\frac31</math>, because those are the target-intervals corresponding to its nonzero entries. And both nonzero entries are <math>+1</math> meaning that both target-intervals are combined in the same direction. In other words, <math>\frac21 × \frac31 = \frac61</math> is going to have something to do with this (and we're finally about to find out what that is!). | ||
Line 5,811: | Line 5,734: | ||
===The meaning of the ReDPOTIC product=== | === The meaning of the ReDPOTIC product === | ||
And that's our tuning, the tuning found at this coinciding-damage point. | And that's our tuning, the tuning found at this coinciding-damage point. | ||
Line 5,821: | Line 5,743: | ||
And so that's what the constraint matrix's ReDPOTIC product <math>\frac61</math> had to do with things: this product is an ''unchanged-interval'' of this tuning (the only one, in fact). | And so that's what the constraint matrix's ReDPOTIC product <math>\frac61</math> had to do with things: this product is an ''unchanged-interval'' of this tuning (the only one, in fact). | ||
But our original intention here was to find the tuning where <math>\frac21</math> and <math>\frac31</math> have coinciding damage. Well, it turns out this is an equivalent situation. | But our original intention here was to find the tuning where <math>\frac21</math> and <math>\frac31</math> have coinciding damage. Well, it turns out this is an equivalent situation. If—according to this temperament's mapping {{map|12 19 28}}—it takes 12 steps to reach <math>\frac21</math> and also it takes 19 steps to reach <math>\frac31</math>, and that it therefore takes 31 steps to reach a <math>\frac61</math>, and it is also the case that <math>\frac61</math> is pure, then that implies that whatever error there is on <math>\frac21</math> must be the exact opposite of whatever damage there is on <math>\frac31</math>, since their errors apparently cancel out. So if their errors are exact opposites—negations—then their damages are the same. So we achieve coinciding target-interval damages via an unchanged-interval that they all relate to. Cue the success fanfare. | ||
=== Applying a constraint for a zero-damage point === | |||
Let's also try applying a <math>K</math> for a zero-damage point, i.e. one that came from a STIC. | Let's also try applying a <math>K</math> for a zero-damage point, i.e. one that came from a STIC. | ||
Line 5,947: | Line 5,868: | ||
===Comparing the zero-damage method's unchanged-interval bases with the coinciding-damage method's constraint matrices=== | === Comparing the zero-damage method's unchanged-interval bases with the coinciding-damage method's constraint matrices === | ||
If you recall, the zero-damage method for miniaverage tunings works by directly assembling unchanged-interval bases <math>\mathrm{U}</math> out of combinations of target-intervals. The coinciding-damage method here, however, indirectly achieves unchanged-interval bases via constraint matrices <math>K</math>. It does this both for the zero-damage points such as are used by the zero-damage method, as well as for the general coinciding-damage points that the zero-damage method does not use. | If you recall, the zero-damage method for miniaverage tunings works by directly assembling unchanged-interval bases <math>\mathrm{U}</math> out of combinations of target-intervals. The coinciding-damage method here, however, indirectly achieves unchanged-interval bases via constraint matrices <math>K</math>. It does this both for the zero-damage points such as are used by the zero-damage method, as well as for the general coinciding-damage points that the zero-damage method does not use. | ||
Line 5,955: | Line 5,875: | ||
The zero-damage method might have been designed to use constraint matrices, but this would probably be overkill. When general coinciding-damage points are not needed, it's simpler to use unchanged-interval bases directly. | The zero-damage method might have been designed to use constraint matrices, but this would probably be overkill. When general coinciding-damage points are not needed, it's simpler to use unchanged-interval bases directly. | ||
===Get damage lists=== | === Get damage lists === | ||
From here, we basically just need to take ''every'' tuning we find from the linear solutions like this, and for each one, find its target-interval damage list, and then from that find its maximum damage. | From here, we basically just need to take ''every'' tuning we find from the linear solutions like this, and for each one, find its target-interval damage list, and then from that find its maximum damage. | ||
Line 6,036: | Line 5,955: | ||
For each damage list, we can find the coinciding damages. In the first tuning, it's the first two target-intervals' damages, both with <math>0.757</math>. In the fifth tuning, it's the second and third target-intervals' damages, both with <math>6.697</math>. Etcetera. Note that these coinciding damages are not necessarily the ''max'' damages of the tuning; for example, the third tuning shows the first and third target-intervals both equal to <math>4.106</math> damage, but the second interval has more than twice that, at <math>8.456</math> damage. That's fine. In many cases, in fact, the tuning we ultimately want is one of these where the coinciding damages are not the max damages. | For each damage list, we can find the coinciding damages. In the first tuning, it's the first two target-intervals' damages, both with <math>0.757</math>. In the fifth tuning, it's the second and third target-intervals' damages, both with <math>6.697</math>. Etcetera. Note that these coinciding damages are not necessarily the ''max'' damages of the tuning; for example, the third tuning shows the first and third target-intervals both equal to <math>4.106</math> damage, but the second interval has more than twice that, at <math>8.456</math> damage. That's fine. In many cases, in fact, the tuning we ultimately want is one of these where the coinciding damages are not the max damages. | ||
===Identify minimax=== | === Identify minimax === | ||
In order to identify the minimax is generally pretty straightforward. We gather up all the maxes. And pick their min. That's the min-i-max. | In order to identify the minimax is generally pretty straightforward. We gather up all the maxes. And pick their min. That's the min-i-max. | ||
Line 6,062: | Line 5,980: | ||
Had there been a tie here, i.e. had some other tuning besides <math>𝒈_5</math> also had 6.697 ¢ for its maximum damage, such that more than one tuning tied for minimax, then we would need to move on to tie-breaking. That gets very involved, so we'll look at that in detail in a later section.. | Had there been a tie here, i.e. had some other tuning besides <math>𝒈_5</math> also had 6.697 ¢ for its maximum damage, such that more than one tuning tied for minimax, then we would need to move on to tie-breaking. That gets very involved, so we'll look at that in detail in a later section.. | ||
==A bigger example== | == A bigger example == | ||
The rank-1 temperament case we've just worked through, which has just one generator, was a great introduction, but a bit too simple to demonstrate some of the ideas we want to touch upon here. | The rank-1 temperament case we've just worked through, which has just one generator, was a great introduction, but a bit too simple to demonstrate some of the ideas we want to touch upon here. | ||
* Some aspects of the constraint matrices require multiple generators in order to illustrate effectively. | * Some aspects of the constraint matrices require multiple generators in order to illustrate effectively. | ||
Line 6,078: | Line 5,995: | ||
* and get our answer in the form of a generator embedding. | * and get our answer in the form of a generator embedding. | ||
===Prepare constraint matrix=== | === Prepare constraint matrix === | ||
If we have three generators, we will have ''many'' coinciding-damage points, each one corresponding to its own constraint matrix <math>K</math>. For this example, we're not going to bother showing all of them. It would be way too much to show. Let's just follow the logic from start to finish with a single constraint matrix. | If we have three generators, we will have ''many'' coinciding-damage points, each one corresponding to its own constraint matrix <math>K</math>. For this example, we're not going to bother showing all of them. It would be way too much to show. Let's just follow the logic from start to finish with a single constraint matrix. | ||
Line 6,124: | Line 6,040: | ||
Here's something important to observe that we couldn't confront yet with the simpler single-generator example. Note that while there is always one row of the constraint matrix for each generator, ''each row of the constraint matrix has no particular association with any one of the generators''. In other words, it wouldn't make sense for us to label the first column of this <math>K</math> with <math>g_1</math>, the second with <math>g_2</math>, and the third with <math>g_3</math> (or any other ordering of those); each column is as relevant to one of those generators as it is to any other. Any one of the generators may turn out to be the one which satisfies one of these constraints. Said another way, when we perform matrix multiplication between this <math>K</math> matrix and the <math>M\mathrm{T}W</math> situation, each row of <math>K</math> touches each row of <math>M\mathrm{T}W</math>, so <math>K</math>'s influence is exerted across the board. | Here's something important to observe that we couldn't confront yet with the simpler single-generator example. Note that while there is always one row of the constraint matrix for each generator, ''each row of the constraint matrix has no particular association with any one of the generators''. In other words, it wouldn't make sense for us to label the first column of this <math>K</math> with <math>g_1</math>, the second with <math>g_2</math>, and the third with <math>g_3</math> (or any other ordering of those); each column is as relevant to one of those generators as it is to any other. Any one of the generators may turn out to be the one which satisfies one of these constraints. Said another way, when we perform matrix multiplication between this <math>K</math> matrix and the <math>M\mathrm{T}W</math> situation, each row of <math>K</math> touches each row of <math>M\mathrm{T}W</math>, so <math>K</math>'s influence is exerted across the board. | ||
Another thing to note is that we set up the constraint matrix so that there's one target-interval that has a non-zero entry in each row, and that this is also the ''first'' target-interval column with a non-zero entry, i.e. the one that's been anchored to the positive direction. As we can see, in our case, that target-interval is <math>\frac75</math>. Setting up our constraint matrix in this way is how we | Another thing to note is that we set up the constraint matrix so that there's one target-interval that has a non-zero entry in each row, and that this is also the ''first'' target-interval column with a non-zero entry, i.e. the one that's been anchored to the positive direction. As we can see, in our case, that target-interval is <math>\frac75</math>. Setting up our constraint matrix in this way is how we establish—using the transitive property of equality—that all four of these target-intervals with non-zero entries somewhere their column will have coinciding (equal) damages. Because if A's damage equals B's, and B's damage equals C's, then we also know that A's damage equals C's. And same for D. So we end up with A's damage = B's damage = C's damage = D's damage. All four have coinciding damage. | ||
Eventually we want to multiply this constraint matrix by <math>M\mathrm{T}W</math> and by <math>\mathrm{T}W</math>. So let's look at those next. | Eventually we want to multiply this constraint matrix by <math>M\mathrm{T}W</math> and by <math>\mathrm{T}W</math>. So let's look at those next. | ||
===Prepare tempered and just sides of to-be equality=== | === Prepare tempered and just sides of to-be equality === | ||
For our mapping, let's use the [[mingen|minimal generator form]] of [[Breed_family#Breed|breed temperament]], and for weights, let's use complexity-weighted damage (<math>W = C</math>). | For our mapping, let's use the [[mingen|minimal generator form]] of [[Breed_family#Breed|breed temperament]], and for weights, let's use complexity-weighted damage (<math>W = C</math>). | ||
Line 6,230: | Line 6,145: | ||
===Apply constraint=== | === Apply constraint === | ||
Now we've got to constrain both sides of the problem. First the left side: | Now we've got to constrain both sides of the problem. First the left side: | ||
Line 6,367: | Line 6,281: | ||
===Solve for generator embedding=== | === Solve for generator embedding === | ||
To solve for <math>G</math>, we take the inverse of <math>M\mathrm{T}CK</math> and right-multiply both sides of the equation by it. This will cancel it out on the left-hand side, isolating <math>G</math>: | To solve for <math>G</math>, we take the inverse of <math>M\mathrm{T}CK</math> and right-multiply both sides of the equation by it. This will cancel it out on the left-hand side, isolating <math>G</math>: | ||
Line 6,527: | Line 6,440: | ||
Egads! | Egads! | ||
===Convert generator embedding to generator map=== | === Convert generator embedding to generator map === | ||
Taking the values from the first column of this, we can find that our first generator, <math>\textbf{g}_1</math>, is exactly equal to: | Taking the values from the first column of this, we can find that our first generator, <math>\textbf{g}_1</math>, is exactly equal to: | ||
Line 6,625: | Line 6,537: | ||
And so that's the tuning (in cents) we find for this constraint matrix! (But remember, this is only one of many candidates for the minimax tuning | And so that's the tuning (in cents) we find for this constraint matrix! (But remember, this is only one of many candidates for the minimax tuning here—it is not necessarily the actual minimax tuning. We picked this particular ReDPOTIC / constraint matrix / coinciding-damage point / candidate tuning example basically at random.) | ||
=== System of equations style === | |||
In our simpler example, we looked at our matrix equation as a system of equations. It may be instructive to consider this related approach toward this result. | In our simpler example, we looked at our matrix equation as a system of equations. It may be instructive to consider this related approach toward this result. | ||
Line 6,682: | Line 6,593: | ||
The system of equations style is how Keenan's original algorithm works. The matrix inverse style is Dave's modification which may be less obvious how it works, but is capable of solving for generator embeddings. | The system of equations style is how Keenan's original algorithm works. The matrix inverse style is Dave's modification which may be less obvious how it works, but is capable of solving for generator embeddings. | ||
===Sanity-check=== | === Sanity-check === | ||
We can sanity check it if we like. This was supposed to find us the tuning of breed temperament where <math>\frac75 × \frac85 = \frac{56}{25}</math>, <math>\frac75 × \frac59 = \frac79</math> (or we might prefer to think of it in its superunison form, <math>\frac97</math>), and <math>\frac75 × \frac34 = \frac{21}{20}</math> are pure. | We can sanity check it if we like. This was supposed to find us the tuning of breed temperament where <math>\frac75 × \frac85 = \frac{56}{25}</math>, <math>\frac75 × \frac59 = \frac79</math> (or we might prefer to think of it in its superunison form, <math>\frac97</math>), and <math>\frac75 × \frac34 = \frac{21}{20}</math> are pure. | ||
Line 6,692: | Line 6,602: | ||
Finally breed maps <math>\textbf{i}_3 = \frac{21}{20}</math> {{vector|-2 1 -1 1}} to the generator-count vector <math>\textbf{y}_3</math> {{rket|0 1 -1}}. And <math>𝒈\textbf{y}_3</math> looks like {{rbra|1198.679 350.516 265.929}}{{rket|0 1 -1}} <math>= 1198.679 × 0 + 350.516 × 1 + 265.929 × {-1} = 84.587</math>. Its JI size is <math>1200 × \log_2(\frac{21}{20}) = 84.467</math>, again, essentially pure. | Finally breed maps <math>\textbf{i}_3 = \frac{21}{20}</math> {{vector|-2 1 -1 1}} to the generator-count vector <math>\textbf{y}_3</math> {{rket|0 1 -1}}. And <math>𝒈\textbf{y}_3</math> looks like {{rbra|1198.679 350.516 265.929}}{{rket|0 1 -1}} <math>= 1198.679 × 0 + 350.516 × 1 + 265.929 × {-1} = 84.587</math>. Its JI size is <math>1200 × \log_2(\frac{21}{20}) = 84.467</math>, again, essentially pure. | ||
===Relation to only-held intervals method and zero-damage method=== | === Relation to only-held intervals method and zero-damage method === | ||
Note that this <math>G = \mathrm{T}CK(M\mathrm{T}CK)^{-1}</math> formula is the same thing as the <math>G = U(MU)^{-1}</math> formula used for the [[#only held-intervals method]]; it's just that formula where <math>\mathrm{U} = \mathrm{T}CK</math>. In other words, we will find a basis for the unchanged intervals of this tuning of this temperament to be: | Note that this <math>G = \mathrm{T}CK(M\mathrm{T}CK)^{-1}</math> formula is the same thing as the <math>G = U(MU)^{-1}</math> formula used for the [[#only held-intervals method]]; it's just that formula where <math>\mathrm{U} = \mathrm{T}CK</math>. In other words, we will find a basis for the unchanged intervals of this tuning of this temperament to be: | ||
Line 6,714: | Line 6,623: | ||
So, in effect, the coinciding-damage method is fairly similar to the zero-damage method. Each point in either method's point set corresponds to an unchanged-interval basis <math>\mathrm{U}</math>. It is the case that for the zero-damage method the members of this <math>\mathrm{U}</math> are pulled directly from the target-interval set <math>\mathrm{T}</math>, whereas for the coinciding-damage method here, the members of each <math>\mathrm{U}</math> have a more complex relationship with the members of <math>\mathrm{T}</math> set, being products of relative direction pairs of them instead. | So, in effect, the coinciding-damage method is fairly similar to the zero-damage method. Each point in either method's point set corresponds to an unchanged-interval basis <math>\mathrm{U}</math>. It is the case that for the zero-damage method the members of this <math>\mathrm{U}</math> are pulled directly from the target-interval set <math>\mathrm{T}</math>, whereas for the coinciding-damage method here, the members of each <math>\mathrm{U}</math> have a more complex relationship with the members of <math>\mathrm{T}</math> set, being products of relative direction pairs of them instead. | ||
==With held-intervals== | == With held-intervals == | ||
When a tuning scheme has optimization power <math>p = ∞</math> and also specifies one or more held-intervals, we can adapt the coinciding-damage method to accommodate this. In short, we can no longer dedicate every generator toward our target-intervals; we must allocate one generator toward each interval to be held unchanged. | When a tuning scheme has optimization power <math>p = ∞</math> and also specifies one or more held-intervals, we can adapt the coinciding-damage method to accommodate this. In short, we can no longer dedicate every generator toward our target-intervals; we must allocate one generator toward each interval to be held unchanged. | ||
===Counts of target-intervals with held-intervals=== | === Counts of target-intervals with held-intervals === | ||
In the earlier section [[#Generalizing to higher dimensions: counts of target-intervals required to make the points]], we looked at how it generally takes <math>r + 1</math> target-interval damage graphs to intersect to make a point, but only <math>r</math> of them on the zero-damage floor. | In the earlier section [[#Generalizing to higher dimensions: counts of target-intervals required to make the points]], we looked at how it generally takes <math>r + 1</math> target-interval damage graphs to intersect to make a point, but only <math>r</math> of them on the zero-damage floor. | ||
Line 6,730: | Line 6,637: | ||
In that very simple example, only one of the temperament's generators was involved in the mapped interval that the held-interval maps to, so the cross-section is conveniently perpendicular to the axis for that generator, and thus the tuning damage graph with reduced dimension is easy to prepare. However, if we had instead requested a held-{5/4}, then since that maps to {{rket|-2 4}}, using multiple different generators, then the cross-section will be diagonal across the floor, perpendicular to no generator axes. | In that very simple example, only one of the temperament's generators was involved in the mapped interval that the held-interval maps to, so the cross-section is conveniently perpendicular to the axis for that generator, and thus the tuning damage graph with reduced dimension is easy to prepare. However, if we had instead requested a held-{5/4}, then since that maps to {{rket|-2 4}}, using multiple different generators, then the cross-section will be diagonal across the floor, perpendicular to no generator axes. | ||
===Modified constraint matrices=== | === Modified constraint matrices === | ||
We do this by changing our constraint matrices <math>K</math>; rather than building them to represent permutations of relative direction for combinations of <math>r + 1</math> target-intervals, instead we only combine <math>r + 1 - h</math> target-intervals for each of these constraint matrices, where <math>h</math> is the count of held-intervals. As a result of this, each <math>K</math> has <math>h</math> fewer columns than before—or at least it would, if we didn't replace these columns with <math>h</math> new columns, one for each of our <math>h</math> held-intervals. Remember, each of these constraint matrices gets multiplied together with other matrices that represent information about our temperament and tuning scheme—our targeted intervals, and their weights (if any)—in order to take a system of approximations (represented by matrices) and crunch it down to a smaller system of equalities (still represented by matrices) that can be automatically solved (using a matrix inverse). So, instead of these constraint matrices doing only a single job—enforcing that <math>r + 1</math> target-intervals receive coinciding damage, each constraint matrix now handles two jobs at once—enforcing that only <math>r + 1 - h</math> target-intervals receive coinciding damage, and that <math>h</math> held-intervals receive zero damage. | |||
We do this by changing our constraint matrices <math>K</math>; rather than building them to represent permutations of relative direction for combinations of <math>r + 1</math> target-intervals, instead we only combine <math>r + 1 - h</math> target-intervals for each of these constraint matrices, where <math>h</math> is the count of held-intervals. As a result of this, each <math>K</math> has <math>h</math> fewer columns than | |||
In order for the constraint matrices to handle their new second job, however, we must make further changes. The held-intervals must now be accessible in the other matrices that multiply together to form our solvable system of equations. In particular: | In order for the constraint matrices to handle their new second job, however, we must make further changes. The held-intervals must now be accessible in the other matrices that multiply together to form our solvable system of equations. In particular: | ||
# We concatenate the target-interval list <math>\mathrm{T}</math> and the held-interval basis <math>\mathrm{H}</math> together to a new matrix <math>\mathrm{T}|\mathrm{H}</math>. | # We concatenate the target-interval list <math>\mathrm{T}</math> and the held-interval basis <math>\mathrm{H}</math> together to a new matrix <math>\mathrm{T}|\mathrm{H}</math>. | ||
# We accordingly extend the weight matrix <math>W</math> diagonally so that matrix shapes work out for multiplication purposes, or said another way, so that each held-interval appended to <math>\mathrm{T}</math> gets matched up with a dummy weight.<ref>This weight is irrelevant, since these aren't really target-intervals, they're held-intervals, and so the damage to them must be 0; we can choose any value for this weight other than 0 and the effect will be the same, so we may as well choose 1).</ref> | # We accordingly extend the weight matrix <math>W</math> diagonally so that matrix shapes work out for multiplication purposes, or said another way, so that each held-interval appended to <math>\mathrm{T}</math> gets matched up with a dummy weight.<ref group="note">This weight is irrelevant, since these aren't really target-intervals, they're held-intervals, and so the damage to them must be 0; we can choose any value for this weight other than 0 and the effect will be the same, so we may as well choose 1).</ref> | ||
=== Prepare constraint matrix === | |||
Let's demonstrate this by example. We'll revise the example we looked at in the earlier "bigger example" section, with 3 generators (<math>r = 3</math>) and 10 target-intervals (<math>k = 10</math>). The specific example constraint matrix we looked at, therefore, was an <math>(k, r)</math>-shaped matrix, which related <math>r + 1 = 4</math> of those target-intervals together with coinciding damage: | Let's demonstrate this by example. We'll revise the example we looked at in the earlier "bigger example" section, with 3 generators (<math>r = 3</math>) and 10 target-intervals (<math>k = 10</math>). The specific example constraint matrix we looked at, therefore, was an <math>(k, r)</math>-shaped matrix, which related <math>r + 1 = 4</math> of those target-intervals together with coinciding damage: | ||
Line 6,841: | Line 6,746: | ||
Oh, | Oh, right—we didn't ''have'' a row for this held-interval yet. All the rows we had before were for our ''target''-intervals. No big deal, though. We just added an extra row for this, our ''held''-interval, and we can fill out the new entries it creates in the other columns with zeros. | ||
=== Modified tempered and just sides of to-be equality === | |||
The consequences of adding this row are more far-reaching than our <math>K</math> matrices, however. Remember, these multiply with <math>M\mathrm{T}C</math> on the left-hand side of the equal sign and <math>\mathrm{T}C</math> on the right-hand side. So matrix shapes have to keep matching for matrix multiplication to remain possible. We just changed the shape of <math>K</math> from an <math>(k, r)</math>-shaped matrix to a <math>(k + h, r)</math>-shaped matrix. The shape of <math>M\mathrm{T}C</math> is <math>(r, k)</math> and the shape of <math>\mathrm{T}C</math> is <math>(d, k)</math>. So if we want these shapes to keep matching, we need to change them to <math>(r, k + h)</math> and <math>(d, k + h)</math>, respectively. | The consequences of adding this row are more far-reaching than our <math>K</math> matrices, however. Remember, these multiply with <math>M\mathrm{T}C</math> on the left-hand side of the equal sign and <math>\mathrm{T}C</math> on the right-hand side. So matrix shapes have to keep matching for matrix multiplication to remain possible. We just changed the shape of <math>K</math> from an <math>(k, r)</math>-shaped matrix to a <math>(k + h, r)</math>-shaped matrix. The shape of <math>M\mathrm{T}C</math> is <math>(r, k)</math> and the shape of <math>\mathrm{T}C</math> is <math>(d, k)</math>. So if we want these shapes to keep matching, we need to change them to <math>(r, k + h)</math> and <math>(d, k + h)</math>, respectively. | ||
Line 6,896: | Line 6,800: | ||
There's no need to mess with <math>M</math> here. | There's no need to mess with <math>M</math> here. | ||
===Prepare tempered and just sides of to-be equality=== | === Prepare tempered and just sides of to-be equality === | ||
As before, let's work out <math>M\mathrm{T}C</math> and <math>\mathrm{T}C</math> separately, then constrain each of them with <math>K</math>, then put them together in a linear system of equations, solving for the generator tuning map <math>𝒈</math>. Though this time around, all occurrences of <math>\mathrm{T}</math> will be replaced with <math>\mathrm{T|\style{background-color:#FFF200;padding:2px}{H}}</math>. | As before, let's work out <math>M\mathrm{T}C</math> and <math>\mathrm{T}C</math> separately, then constrain each of them with <math>K</math>, then put them together in a linear system of equations, solving for the generator tuning map <math>𝒈</math>. Though this time around, all occurrences of <math>\mathrm{T}</math> will be replaced with <math>\mathrm{T|\style{background-color:#FFF200;padding:2px}{H}}</math>. | ||
Line 6,985: | Line 6,888: | ||
\end{array} | \end{array} | ||
= | = \\[50pt] \small | ||
\begin{array} {ccc} | \begin{array} {ccc} | ||
Line 7,000: | Line 6,903: | ||
Again, same as before, but with one more column on the right now. | Again, same as before, but with one more column on the right now. | ||
=== Apply constraint === | |||
Now, let's constrain both sides. First, the left side: | Now, let's constrain both sides. First, the left side: | ||
Line 7,145: | Line 7,047: | ||
===Solve for generator embedding=== | === Solve for generator embedding === | ||
At this point, following the pattern from above, we can solve for <math>G</math> as <math>(\mathrm{T|\style{background-color:#FFF200;padding:2px}{H}})CK(M(\mathrm{T|\style{background-color:#FFF200;padding:2px}{H}})CK)^{-1}</math>. The matrix inverse is the step where the exact values in terms of logarithms start to get out of hand. Since we've already proven our point about exactness of the solutions from this method in the earlier "bigger example" section, for easier reading, let's lapse into decimal numbers from this point forward: | At this point, following the pattern from above, we can solve for <math>G</math> as <math>(\mathrm{T|\style{background-color:#FFF200;padding:2px}{H}})CK(M(\mathrm{T|\style{background-color:#FFF200;padding:2px}{H}})CK)^{-1}</math>. The matrix inverse is the step where the exact values in terms of logarithms start to get out of hand. Since we've already proven our point about exactness of the solutions from this method in the earlier "bigger example" section, for easier reading, let's lapse into decimal numbers from this point forward: | ||
Line 7,205: | Line 7,106: | ||
===Convert generator embedding to generator tuning map=== | === Convert generator embedding to generator tuning map === | ||
And from here we can find <math>𝒈 = 𝒋G</math>: | And from here we can find <math>𝒈 = 𝒋G</math>: | ||
Line 7,243: | Line 7,143: | ||
Confirming: yes, {{rbra|1200.000 350.909 266.725}}{{rket|0 1 2}} = <math>(1200.000 × 0) + (350.909 × 1) + (266.725 × 2) = 0 + 350.909 + 533.450 = 884.359</math>. So the constraint to hold that interval unchanged has been satisfied. | Confirming: yes, {{rbra|1200.000 350.909 266.725}}{{rket|0 1 2}} = <math>(1200.000 × 0) + (350.909 × 1) + (266.725 × 2) = 0 + 350.909 + 533.450 = 884.359</math>. So the constraint to hold that interval unchanged has been satisfied. | ||
===System of equations style=== | === System of equations style === | ||
It may be instructive again to consider the system of equations style, solving directly for <math>𝒈</math>. Rewinding to before we took our matrix inverse, introducing <math>𝒋</math> to both sides, and multiplying the entries of <math>𝒈</math> through, we find: | It may be instructive again to consider the system of equations style, solving directly for <math>𝒈</math>. Rewinding to before we took our matrix inverse, introducing <math>𝒋</math> to both sides, and multiplying the entries of <math>𝒈</math> through, we find: | ||
Line 7,282: | Line 7,181: | ||
The third | The third equation—which in the earlier "bigger example" was used to enforce that a fourth target-interval received coinciding damage to the other three target-intervals tapped by the constraint matrix—here has been replaced with an equation that enforces that {{rket|0 1 2}}, the generator-count vector that <math>\frac53</math> maps to in this temperament, is equal to the just tuning of that same interval. And so, when we solve this system of equations, we now get a completely different set of generator tunings. | ||
Line 7,294: | Line 7,193: | ||
Which agrees with what we found by solving directly for <math>G</math>. | Which agrees with what we found by solving directly for <math>G</math>. | ||
===Sanity-check=== | === Sanity-check === | ||
But is this a minimax tuning candidate still? That is, do we find that in the damage list for this tuning, that the three target-intervals whose damages were requested to coincide, are still coinciding? Indeed we do: | But is this a minimax tuning candidate still? That is, do we find that in the damage list for this tuning, that the three target-intervals whose damages were requested to coincide, are still coinciding? Indeed we do: | ||
Line 7,311: | Line 7,209: | ||
We can see that the 2<sup>nd</sup>, 3<sup>rd</sup>, and 4<sup>th</sup> target-intervals, the ones with non-zero entries in their columns of <math>K</math>, all coincide with 0.742 damage, so this is a proper candidate for minimax tuning here. It's some point where three damage graphs are intersecting while within the held-interval plane. Rinse and repeat. | We can see that the 2<sup>nd</sup>, 3<sup>rd</sup>, and 4<sup>th</sup> target-intervals, the ones with non-zero entries in their columns of <math>K</math>, all coincide with 0.742 damage, so this is a proper candidate for minimax tuning here. It's some point where three damage graphs are intersecting while within the held-interval plane. Rinse and repeat. | ||
==Tie-breaking== | == Tie-breaking == | ||
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 [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Tie_breaking:_power_limit_method|power-limit method]], which can only provide an approximate solution. | |||
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 [[ | |||
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: | 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: | ||
Line 7,322: | Line 7,219: | ||
[[File:Basic advanced tiebreaking.png|thumbnail|600x600px]] | [[File:Basic advanced tiebreaking.png|none|thumbnail|600x600px]] | ||
Let's just dive into an example. | === Basic tie-breaking example: Setup === | ||
Let's just dive into an example. | |||
The example given in the diagrams back in article 3 [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning fundamentals#How_to_choose_the_true_optimum|here]] and article 6 [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Tie_breaking:_power_limit_method|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>\{ \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>\frac21</math>, the blue hyper-V is for <math>\frac31</math>, the green hyper-V is for <math>\frac51</math>, and the red hyper-V is for <math>\frac65</math>. | Suppose we're doing a minimax-U tuning of blackwood temperament, and our target-interval set is <math>\{ \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>\frac21</math>, the blue hyper-V is for <math>\frac31</math>, the green hyper-V is for <math>\frac51</math>, and the red hyper-V is for <math>\frac65</math>. | ||
Line 7,341: | Line 7,237: | ||
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>\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. | 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>\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: | 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: | ||
{| class="wikitable center-all mw-collapsible" | {| class="wikitable center-all mw-collapsible" | ||
|+ | |+ | ||
! | ! Constraint matrix | ||
! colspan="2" | | ! colspan="2" | Candidate generator tuning map | ||
! | ! Target-interval damage list | ||
|- | |- | ||
! <math>K</math> | |||
! <math>𝒈_i</math> | |||
! {{rbra|<math>g_1</math> <math>g_2</math>}} | |||
! <math>\textbf{d}</math> | |||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & +1 \\ +1 & 0 \\ 0 & -1 \\ 0 & 0 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & +1 \\ +1 & 0 \\ 0 & +1 \\ 0 & 0 \\ \end{array} \right]</math> | ||
|<math>𝒈_2</math> | | <math>𝒈_1</math> | ||
|{{rbra|238.612 2779.374}} | | {{rbra|238.612 2793.254}} | ||
|[6.940 6.940 6.940 6.940] | | [6.940 6.940 6.940 6.940] | ||
|- | |||
| <math>\left[ \begin{array} {rrr} +1 & +1 \\ +1 & 0 \\ 0 & -1 \\ 0 & 0 \\ \end{array} \right]</math> | |||
| <math>𝒈_2</math> | |||
| {{rbra|238.612 2779.374}} | |||
| [6.940 6.940 6.940 6.940] | |||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & +1 \\ -1 & 0 \\ 0 & +1 \\ 0 & 0 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & +1 \\ -1 & 0 \\ 0 & +1 \\ 0 & 0 \\ \end{array} \right]</math> | ||
|<math>𝒈_3</math> | | <math>𝒈_3</math> | ||
|{{rbra|233.985 2816.390}} | | {{rbra|233.985 2816.390}} | ||
|[30.075 30.075 30.075 30.075] | | [30.075 30.075 30.075 30.075] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & +1 \\ -1 & 0 \\ 0 & -1 \\ 0 & 0 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & +1 \\ -1 & 0 \\ 0 & -1 \\ 0 & 0 \\ \end{array} \right]</math> | ||
|<math>𝒈_4</math> | | <math>𝒈_4</math> | ||
|{{rbra|233.985 2756.240}} | | {{rbra|233.985 2756.240}} | ||
|[30.075 30.075 30.075 30.075] | | [30.075 30.075 30.075 30.075] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & +1 \\ +1 & 0 \\ 0 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & +1 \\ +1 & 0 \\ 0 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | ||
|<math>𝒈_2</math> | | <math>𝒈_2</math> | ||
|{{rbra|238.612 2779.374}} | | {{rbra|238.612 2779.374}} | ||
|[6.940 6.940 6.940 6.940] | | [6.940 6.940 6.940 6.940] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & +1 \\ +1 & 0 \\ 0 & 0 \\ 0 & -1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & +1 \\ +1 & 0 \\ 0 & 0 \\ 0 & -1 \\ \end{array} \right]</math> | ||
|<math>𝒈_1</math> | | <math>𝒈_1</math> | ||
|{{rbra|238.612 2793.254}} | | {{rbra|238.612 2793.254}} | ||
|[6.940 6.940 6.940 6.940] | | [6.940 6.940 6.940 6.940] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & +1 \\ -1 & 0 \\ 0 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & +1 \\ -1 & 0 \\ 0 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | ||
|<math>𝒈_5</math> | | <math>𝒈_5</math> | ||
|{{rbra|233.985 2696.090}} | | {{rbra|233.985 2696.090}} | ||
|[30.075 30.075 90.225 30.075] | | [30.075 30.075 90.225 30.075] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & +1 \\ -1 & 0 \\ 0 & 0 \\ 0 & -1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & +1 \\ -1 & 0 \\ 0 & 0 \\ 0 & -1 \\ \end{array} \right]</math> | ||
|<math>𝒈_4</math> | | <math>𝒈_4</math> | ||
|{{rbra|233.985 2756.240}} | | {{rbra|233.985 2756.240}} | ||
|[30.075 30.075 30.075 30.075] | | [30.075 30.075 30.075 30.075] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & +1 \\ 0 & 0 \\ +1 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & +1 \\ 0 & 0 \\ +1 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | ||
|<math>𝒈_6</math> | | <math>𝒈_6</math> | ||
|{{rbra|239.215 2790.240}} | | {{rbra|239.215 2790.240}} | ||
|[3.923 11.769 3.923 3.923] | | [3.923 11.769 3.923 3.923] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & +1 \\ 0 & 0 \\ +1 & 0 \\ 0 & -1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & +1 \\ 0 & 0 \\ +1 & 0 \\ 0 & -1 \\ \end{array} \right]</math> | ||
|<math>𝒈_1</math> | | <math>𝒈_1</math> | ||
|{{rbra|238.612 2793.254}} | | {{rbra|238.612 2793.254}} | ||
|[6.940 6.940 6.940 6.940] | | [6.940 6.940 6.940 6.940] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & +1 \\ 0 & 0 \\ -1 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & +1 \\ 0 & 0 \\ -1 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | ||
|<math>𝒈_2</math> | | <math>𝒈_2</math> | ||
|{{rbra|238.612 2779.374}} | | {{rbra|238.612 2779.374}} | ||
|[6.940 6.940 6.940 6.940] | | [6.940 6.940 6.940 6.940] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & +1 \\ 0 & 0 \\ -1 & 0 \\ 0 & -1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & +1 \\ 0 & 0 \\ -1 & 0 \\ 0 & -1 \\ \end{array} \right]</math> | ||
|<math>𝒈_4</math> | | <math>𝒈_4</math> | ||
|{{rbra|233.985 2756.240}} | | {{rbra|233.985 2756.240}} | ||
|[30.075 30.075 30.075 30.075] | | [30.075 30.075 30.075 30.075] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} 0 & 0 \\ +1 & +1 \\ +1 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} 0 & 0 \\ +1 & +1 \\ +1 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | ||
|<math>𝒈_7</math> | | <math>𝒈_7</math> | ||
|{{rbra|238.133 2783.200}} | | {{rbra|238.133 2783.200}} | ||
|[9.334 3.111 3.111 3.111] | | [9.334 3.111 3.111 3.111] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} 0 & 0 \\ +1 & +1 \\ +1 & 0 \\ 0 & -1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} 0 & 0 \\ +1 & +1 \\ +1 & 0 \\ 0 & -1 \\ \end{array} \right]</math> | ||
|<math>𝒈_2</math> | | <math>𝒈_2</math> | ||
|{{rbra|238.612 2779.374}} | | {{rbra|238.612 2779.374}} | ||
|[6.940 6.940 6.940 6.940] | | [6.940 6.940 6.940 6.940] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} 0 & 0 \\ +1 & +1 \\ -1 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} 0 & 0 \\ +1 & +1 \\ -1 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | ||
|<math>𝒈_1</math> | | <math>𝒈_1</math> | ||
|{{rbra|238.612 2793.254}} | | {{rbra|238.612 2793.254}} | ||
|[6.940 6.940 6.940 6.940] | | [6.940 6.940 6.940 6.940] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} 0 & 0 \\ +1 & +1 \\ -1 & 0 \\ 0 & -1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} 0 & 0 \\ +1 & +1 \\ -1 & 0 \\ 0 & -1 \\ \end{array} \right]</math> | ||
|<math>𝒈_4</math> | | <math>𝒈_4</math> | ||
|{{rbra|233.985 2756.240}} | | {{rbra|233.985 2756.240}} | ||
|[30.075 30.075 30.075 30.075] | | [30.075 30.075 30.075 30.075] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & 0 \\ 0 & +1 \\ 0 & 0 \\ 0 & 0 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & 0 \\ 0 & +1 \\ 0 & 0 \\ 0 & 0 \\ \end{array} \right]</math> | ||
|n/a | | n/a | ||
|n/a | | n/a | ||
|n/a | | n/a | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & 0 \\ 0 & 0 \\ 0 & +1 \\ 0 & 0 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & 0 \\ 0 & 0 \\ 0 & +1 \\ 0 & 0 \\ \end{array} \right]</math> | ||
|<math>𝒈_8</math> | | <math>𝒈_8</math> | ||
|{{rbra|240.000 2786.314}} | | {{rbra|240.000 2786.314}} | ||
|[0.000 18.045 0.000 18.045] | | [0.000 18.045 0.000 18.045] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} +1 & 0 \\ 0 & 0 \\ 0 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} +1 & 0 \\ 0 & 0 \\ 0 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | ||
|<math>𝒈_9</math> | | <math>𝒈_9</math> | ||
|{{rbra|240.000 2804.360}} | | {{rbra|240.000 2804.360}} | ||
|[0.000 18.045 18.045 0.000] | | [0.000 18.045 18.045 0.000] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} 0 & 0 \\ +1 & 0 \\ 0 & +1 \\ 0 & 0 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} 0 & 0 \\ +1 & 0 \\ 0 & +1 \\ 0 & 0 \\ \end{array} \right]</math> | ||
|<math>𝒈_{10}</math> | | <math>𝒈_{10}</math> | ||
|{{rbra|237.744 2786.314}} | | {{rbra|237.744 2786.314}} | ||
|[11.278 0.000 0.000 11.278] | | [11.278 0.000 0.000 11.278] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} 0 & 0 \\ +1 & 0 \\ 0 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} 0 & 0 \\ +1 & 0 \\ 0 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | ||
|<math>𝒈_{11}</math> | | <math>𝒈_{11}</math> | ||
|{{rbra|237.744 2775.040}} | | {{rbra|237.744 2775.040}} | ||
|[11.278 0.000 11.278 0.000] | | [11.278 0.000 11.278 0.000] | ||
|- | |- | ||
|<math>\left[ \begin{array} {rrr} 0 & 0 \\ 0 & 0 \\ +1 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | | <math>\left[ \begin{array} {rrr} 0 & 0 \\ 0 & 0 \\ +1 & 0 \\ 0 & +1 \\ \end{array} \right]</math> | ||
|<math>𝒈_{12}</math> | | <math>𝒈_{12}</math> | ||
|{{rbra|238.612 2786.314}} | | {{rbra|238.612 2786.314}} | ||
|[6.940 6.940 0.000 0.000] | | [6.940 6.940 0.000 0.000] | ||
|} | |} | ||
Line 7,472: | Line 7,367: | ||
{| class="wikitable center-all mw-collapsible" | {| class="wikitable center-all mw-collapsible" | ||
|- | |- | ||
|<math>𝒈_1</math> | ! colspan="2" | Candidate generator tuning map | ||
|{{rbra|238.612 2793.254}} | ! Target-interval damage list | ||
|[6.940 6.940 6.940 6.940] | ! Max damage | ||
|6.940 | |- | ||
! <math>𝒈_i</math> | |||
! {{rbra|<math>g_1</math> <math>g_2</math>}} | |||
! <math>\textbf{d}</math> | |||
! <math>\max(\textbf{d})</math> | |||
|- | |||
| <math>𝒈_1</math> | |||
| {{rbra|238.612 2793.254}} | |||
| [6.940 6.940 6.940 6.940] | |||
| 6.940 | |||
|- | |- | ||
|<math>𝒈_2</math> | | <math>𝒈_2</math> | ||
|{{rbra|238.612 2779.374}} | | {{rbra|238.612 2779.374}} | ||
|[6.940 6.940 6.940 6.940] | | [6.940 6.940 6.940 6.940] | ||
|6.940 | | 6.940 | ||
|- | |- | ||
|<math>𝒈_3</math> | | <math>𝒈_3</math> | ||
|{{rbra|233.985 2816.390}} | | {{rbra|233.985 2816.390}} | ||
|[30.075 30.075 30.075 30.075] | | [30.075 30.075 30.075 30.075] | ||
|30.075 | | 30.075 | ||
|- | |- | ||
|<math>𝒈_4</math> | | <math>𝒈_4</math> | ||
|{{rbra|233.985 2756.240}} | | {{rbra|233.985 2756.240}} | ||
|[30.075 30.075 30.075 30.075] | | [30.075 30.075 30.075 30.075] | ||
|30.075 | | 30.075 | ||
|- | |- | ||
|<math>𝒈_5</math> | | <math>𝒈_5</math> | ||
|{{rbra|233.985 2696.090}} | | {{rbra|233.985 2696.090}} | ||
|[30.075 30.075 90.225 30.075] | | [30.075 30.075 90.225 30.075] | ||
|90.225 | | 90.225 | ||
|- | |- | ||
|<math>𝒈_6</math> | | <math>𝒈_6</math> | ||
|{{rbra|239.215 2790.240}} | | {{rbra|239.215 2790.240}} | ||
|[3.923 11.769 3.923 3.923] | | [3.923 11.769 3.923 3.923] | ||
|11.769 | | 11.769 | ||
|- | |- | ||
|<math>𝒈_7</math> | | <math>𝒈_7</math> | ||
|{{rbra|238.133 2783.200}} | | {{rbra|238.133 2783.200}} | ||
|[9.334 3.111 3.111 3.111] | | [9.334 3.111 3.111 3.111] | ||
|9.334 | | 9.334 | ||
|- | |- | ||
|<math>𝒈_8</math> | | <math>𝒈_8</math> | ||
|{{rbra|240.000 2786.314}} | | {{rbra|240.000 2786.314}} | ||
|[0.000 18.045 0.000 18.045] | | [0.000 18.045 0.000 18.045] | ||
|18.045 | | 18.045 | ||
|- | |- | ||
|<math>𝒈_9</math> | | <math>𝒈_9</math> | ||
|{{rbra|240.000 2804.360}} | | {{rbra|240.000 2804.360}} | ||
|[0.000 18.045 18.045 0.000] | | [0.000 18.045 18.045 0.000] | ||
|18.045 | | 18.045 | ||
|- | |- | ||
|<math>𝒈_{10}</math> | | <math>𝒈_{10}</math> | ||
|{{rbra|237.744 2786.314}} | | {{rbra|237.744 2786.314}} | ||
|[11.278 0.000 0.000 11.278] | | [11.278 0.000 0.000 11.278] | ||
|11.278 | | 11.278 | ||
|- | |- | ||
|<math>𝒈_{11}</math> | | <math>𝒈_{11}</math> | ||
|{{rbra|237.744 2775.040}} | | {{rbra|237.744 2775.040}} | ||
|[11.278 0.000 11.278 0.000] | | [11.278 0.000 11.278 0.000] | ||
|11.278 | | 11.278 | ||
|- | |- | ||
|<math>𝒈_{12}</math> | | <math>𝒈_{12}</math> | ||
|{{rbra|238.612 2786.310}} | | {{rbra|238.612 2786.310}} | ||
|[6.940 6.940 0.000 0.000] | | [6.940 6.940 0.000 0.000] | ||
|6.940 | | 6.940 | ||
|} | |} | ||
Line 7,547: | Line 7,442: | ||
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. | 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>𝒈_1</math>, <math>𝒈_2</math>, and <math>𝒈_{12}</math>. These have tuning maps of {{rbra|238.612 2793.254}}, {{rbra|238.612 2779.374}}, and {{rbra|238.612 2786.314}}, respectively. Notice that all three of these give the same tuning for the first generator, <math>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>g_1</math>. As for the tuning of the ''second'' generator <math>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>𝒈_{12}</math>. Its <math>g_2</math> value is 2786.314 ¢, which is in fact ''exactly'' halfway between the other two tuning maps' tuning of <math>g_2</math>, at 2779.374 ¢ and 2793.254 ¢ (you may also notice that 2786.314 is the just tuning of <math>\frac51</math>). | The tied minimax tunings are <math>𝒈_1</math>, <math>𝒈_2</math>, and <math>𝒈_{12}</math>. These have tuning maps of {{rbra|238.612 2793.254}}, {{rbra|238.612 2779.374}}, and {{rbra|238.612 2786.314}}, respectively. Notice that all three of these give the same tuning for the first generator, <math>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>g_1</math>. As for the tuning of the ''second'' generator <math>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>𝒈_{12}</math>. Its <math>g_2</math> value is 2786.314 ¢, which is in fact ''exactly'' halfway between the other two tuning maps' tuning of <math>g_2</math>, at 2779.374 ¢ and 2793.254 ¢ (you may also notice that 2786.314 is the just tuning of <math>\frac51</math>). | ||
Line 7,563: | Line 7,457: | ||
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>\frac21</math> and yellow <math>\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>\frac21</math> and <math>\frac31</math> have opposite error, that means that their errors cancel out when you combine them to the interval <math>\frac61</math>. So <math>\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>\frac51</math> damage graph's crease along the zero-damage floor, <math>\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>\frac61</math> and <math>\frac51</math> are pure there. So what should we expect to find with the red hyper-V here, the one for <math>\frac65</math>? Well, being comprised exactly and only of <math>\frac61</math> and <math>\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. | 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>\frac21</math> and yellow <math>\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>\frac21</math> and <math>\frac31</math> have opposite error, that means that their errors cancel out when you combine them to the interval <math>\frac61</math>. So <math>\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>\frac51</math> damage graph's crease along the zero-damage floor, <math>\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>\frac61</math> and <math>\frac51</math> are pure there. So what should we expect to find with the red hyper-V here, the one for <math>\frac65</math>? Well, being comprised exactly and only of <math>\frac61</math> and <math>\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>𝒈_{12}</math>, the one that also happens to be halfway between <math>𝒈_1</math> and <math>𝒈_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>\frac21</math> and <math>\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>\frac51</math> and <math>\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>\frac51</math> and <math>\frac65</math> as accurately as we can, too? We might as well. And so <math>𝒈_{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 [[#Zero-damage coincidings|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!) | 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>\frac21</math> and <math>\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>\frac51</math> and <math>\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>\frac51</math> and <math>\frac65</math> as accurately as we can, too? We might as well. And so <math>𝒈_{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 [[#Zero-damage coincidings|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!) | ||
Line 7,569: | Line 7,463: | ||
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. | 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. | 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. | ||
Line 7,583: | Line 7,476: | ||
Which brings us finally, to the "A" part of the initialism, for "abbreviated". Specifically, we only keep the first <math>r + 1</math> entries in these lists, and throw out the rest. This is where <math>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. | Which brings us finally, to the "A" part of the initialism, for "abbreviated". Specifically, we only keep the first <math>r + 1</math> entries in these lists, and throw out the rest. This is where <math>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. That's why if these ADSLODs are identical all the way down, we need to try something else, in a new tuning damage space. | 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. | 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>𝒈_1</math>, <math>𝒈_2</math>, and <math>𝒈_{12}</math>, and so we show their whole ADSLODs now: | 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>𝒈_1</math>, <math>𝒈_2</math>, and <math>𝒈_{12}</math>, and so we show their whole ADSLODs now: | ||
Line 7,596: | Line 7,488: | ||
!target-interval damage list | !target-interval damage list | ||
!ADSLOD | !ADSLOD | ||
| | |- | ||
!<math>𝒈_i</math> | !<math>𝒈_i</math> | ||
!{{rbra|<math>g_1</math> <math>g_2</math>}} | !{{rbra|<math>g_1</math> <math>g_2</math>}} | ||
Line 7,621: | Line 7,513: | ||
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>𝒈_1</math> and <math>𝒈_2</math> are still tied for 6.940 even in the third position, with <math>𝒈_{12}</math> we get by with only 0.000. This could be thought of as corresponding either to the damage for <math>\frac51</math> or for <math>\frac65</math>. It doesn't matter which one. At <math>𝒈_1</math> and <math>𝒈_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>\frac21</math> and <math>\frac31</math> (that's why these are the bounds of the minimax range). But at <math>𝒈_{12}</math> one or both of them (we know it's both) is zero. | 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>𝒈_1</math> and <math>𝒈_2</math> are still tied for 6.940 even in the third position, with <math>𝒈_{12}</math> we get by with only 0.000. This could be thought of as corresponding either to the damage for <math>\frac51</math> or for <math>\frac65</math>. It doesn't matter which one. At <math>𝒈_1</math> and <math>𝒈_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>\frac21</math> and <math>\frac31</math> (that's why these are the bounds of the minimax range). But at <math>𝒈_{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. | 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.) | 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>\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.<ref>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>\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.</ref> | The answer is: we would only need to remove <math>\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.<ref group="note">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>\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.</ref> | ||
And why does basic tie-breaking fail without <math>\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. | And why does basic tie-breaking fail without <math>\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. | ||
Line 7,656: | Line 7,549: | ||
===Advanced tie-breaking=== | === 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. | 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. | ||
Line 7,664: | Line 7,556: | ||
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. | 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 {{rket|{{map|3 0 7}} {{map|0 1 0}}}}. In particular, we're going to use the somewhat arbitrary target-interval set <math>\{ \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>g_1</math> is 400 ¢, and our second generator <math>g_2</math> is a free generator for prime 3, so it's going to be ballpark its pure tuning of 1901.955 ¢. | To help illustrate advanced tie-breaking, we're going to look at a minimax-C tuning of [[augmented]] temperament, with mapping {{rket|{{map|3 0 7}} {{map|0 1 0}}}}. In particular, we're going to use the somewhat arbitrary target-interval set <math>\{ \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>g_1</math> is 400 ¢, and our second generator <math>g_2</math> is a free generator for prime 3, so it's going to be ballpark its pure tuning of 1901.955 ¢. | ||
Line 7,700: | Line 7,591: | ||
Spoiler alert: the tuning this crossing identifies is {{rbra|400.171 1902.011}}, which as it should be is somewhere between our previously tied tunings of {{rbra|399.809 1901.289}} and {{rbra|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''. | Spoiler alert: the tuning this crossing identifies is {{rbra|400.171 1902.011}}, which as it should be is somewhere between our previously tied tunings of {{rbra|399.809 1901.289}} and {{rbra|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). | 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). | ||
Line 7,720: | Line 7,610: | ||
And so on to higher and higher dimensions. | 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>x + y + z = 1</math>, then we know <math>x</math> must equal <math>1 - y - z</math>, so that: | 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>x + y + z = 1</math>, then we know <math>x</math> must equal <math>1 - y - z</math>, so that: | ||
Line 7,737: | Line 7,626: | ||
But how would we modify our <math>\mathbf{P} = x\mathbf{A} + y\mathbf{B} + z\mathbf{C}</math> formula to account for this unnecessity of <math>x</math>? | But how would we modify our <math>\mathbf{P} = x\mathbf{A} + y\mathbf{B} + z\mathbf{C}</math> formula to account for this unnecessity of <math>x</math>? | ||
We note that the choice of <math>x</math> | We note that the choice of <math>x</math>—and therefore <math>\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>x</math> with no further modifications; in that case, we'd have <math>\mathbf{P} = \mathbf{A} + y\mathbf{B} + z\mathbf{C}</math>, so now every single point is going to essentially have <math>x = 1</math>, or in other words, a whole <math>\mathbf{A}</math> mixed in. | Clearly we can't simply drop <math>x</math> with no further modifications; in that case, we'd have <math>\mathbf{P} = \mathbf{A} + y\mathbf{B} + z\mathbf{C}</math>, so now every single point is going to essentially have <math>x = 1</math>, or in other words, a whole <math>\mathbf{A}</math> mixed in. | ||
Line 7,768: | Line 7,657: | ||
FYI, the general principle at work with blends here is technically called a "[[Wikipedia:Convex_combination|convex combination]]"; feel free to read more about them now if you're not comfortable with the idea yet. | FYI, the general principle at work with blends here is technically called a "[[Wikipedia:Convex_combination|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 | Okay, and we're almost ready to tie this back to our RTT application. Realize that back in 1D, we'd have | ||
Line 7,788: | Line 7,676: | ||
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. | 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>\mathbf{P}</math>. Let's call it <math>𝒈</math>, simply our generator tuning map. | So we're looking along this line segment for our true optimum tuning, our equivalent of point <math>\mathbf{P}</math>. Let's call it <math>𝒈</math>, simply our generator tuning map. | ||
Line 7,806: | Line 7,693: | ||
=== Generalizing to higher dimensions: The blend map === | |||
Now that's the formula for finding a generator tuning map <math>𝒈</math> somewhere in the line segment between two other tied generator tuning maps <math>𝒈_0</math> and <math>𝒈_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. | Now that's the formula for finding a generator tuning map <math>𝒈</math> somewhere in the line segment between two other tied generator tuning maps <math>𝒈_0</math> and <math>𝒈_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 | It shouldn't be hard to see that for a 2D triangular case—if we wanted to find a <math>𝒈</math> somewhere in between tied <math>𝒈_0</math>, <math>𝒈_1</math>, and <math>𝒈_2</math>—we'd use the formula: | ||
Line 7,824: | Line 7,710: | ||
Well, it is a vector, but in particular its a ''row'' vector, or ''co''vector, 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>𝒃</math> takes the place of the generator tuning map <math>𝒈</math> in iterations of the coinciding-damage method beyond the first. | Well, it is a vector, but in particular its a ''row'' vector, or ''co''vector, 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>𝒃</math> takes the place of the generator tuning map <math>𝒈</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: | Here's the full setup, for an arbitrary count of ties: | ||
Line 7,868: | Line 7,753: | ||
* in the case of a retuning map, both are ''prime'' tuning maps, while in this case both are ''generator'' tuning maps. | * 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>𝜹_i</math> as a substitute for <math>𝒈_i - 𝒈_{0}</math>. We may call it a | We can make use of the Greek letter delta and its association with differences. So let's use <math>𝜹_i</math> as a substitute for <math>𝒈_i - 𝒈_{0}</math>. We may call it a ''delta of generator tuning maps''. The delta <math>𝜹_i</math> takes the index <math>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: | Substituting back into our formula, then, we find: | ||
Line 7,904: | Line 7,789: | ||
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>\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>D</math>, for "delta". | 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>\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>D</math>, for "delta". This is our (generator tuning map) '''deltas matrix'''. | ||
This lets us simplify the formula down to this: | This lets us simplify the formula down to this: | ||
Line 7,913: | Line 7,798: | ||
</math> | </math> | ||
=== How to identify tunings === | |||
This formula assumes we already have all of our tied tunings <math>𝒈_0</math> through <math>𝒈_{τ - 1}</math> from the previous coinciding-damage point set, i.e. the previous iteration of the algorithm. And so we already know the <math>𝒈_0</math> and <math>D</math> parts of this equation. This equation, then, gives us a way to find a tuning <math>𝒈</math> given some blend map <math>𝒃</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. | This formula assumes we already have all of our tied tunings <math>𝒈_0</math> through <math>𝒈_{τ - 1}</math> from the previous coinciding-damage point set, i.e. the previous iteration of the algorithm. And so we already know the <math>𝒈_0</math> and <math>D</math> parts of this equation. This equation, then, gives us a way to find a tuning <math>𝒈</math> given some blend map <math>𝒃</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. | ||
Line 7,986: | Line 7,869: | ||
We've now reached the point where Keenan's original version of this algorithm would solve directly for <math>𝒃</math>, analogous to how it solves directly (and approximately) for <math>𝒈</math> in the first iteration. But when we use the matrix inverse | We've now reached the point where Keenan's original version of this algorithm would solve directly for <math>𝒃</math>, analogous to how it solves directly (and approximately) for <math>𝒈</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>𝒈</math> we instead solve exactly for a generator embedding <math>G</math> and can then later obtain <math>𝒈</math> as <math>𝒈 = 𝒋G</math>—then here we must be solving exactly for some matrix which we could call <math>B</math>, following the analogy <math>𝒈 : G :: 𝒃 : B</math>. (Not to be confused with the subscripted <math>B_s</math> that we use for [[Domain_basis#Basis_matrix_conversion|basis matrices]]; these two matrices will probably never meet, though). This will be a <math>(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>𝒓_0</math> has replaced <math>𝒋</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>𝒓_0</math> is a goal or even a central player in this situation. It just sort of works out this way. | 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>𝒓_0</math> has replaced <math>𝒋</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>𝒓_0</math> is a goal or even a central player in this situation. It just sort of works out this way. | ||
Line 8,037: | Line 7,920: | ||
And that's how you find the generators for a tuning corresponding to a coinciding damage point described by <math>K</math> at whichever point in a tied minimax tuning range it lays. | And that's how you find the generators for a tuning corresponding to a coinciding damage point described by <math>K</math> at whichever point in a tied minimax tuning range it lays. | ||
=== Computing damage === | |||
We ''could'' compute the damage list from any <math>𝒈</math>, as normal: <math>𝐝 = |𝐞|W = |𝒓\mathrm{T}|W = |(𝒕 - 𝒋)\mathrm{T}|W = |(𝒈M - 𝒋)\mathrm{T}|W</math>. But actually we don't ''have'' to recover <math>𝒈</math> from <math>B</math> in order to compute damage. There's a more expedient way to compute it. If: | We ''could'' compute the damage list from any <math>𝒈</math>, as normal: <math>𝐝 = |𝐞|W = |𝒓\mathrm{T}|W = |(𝒕 - 𝒋)\mathrm{T}|W = |(𝒈M - 𝒋)\mathrm{T}|W</math>. But actually we don't ''have'' to recover <math>𝒈</math> from <math>B</math> in order to compute damage. There's a more expedient way to compute it. If: | ||
Line 8,055: | Line 7,937: | ||
And the analogous thing we minimize to make this approximation close (review the [[ | And the analogous thing we minimize to make this approximation close (review the [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Basic_algebraic_setup|basic algebraic setup]] if need be) would be: | ||
Line 8,077: | Line 7,959: | ||
=== 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. | |||
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. | |||
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! | |||
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>𝒈_0</math>, and the second be our <math>𝒈_1</math>: | ||
At the point our basic tie-breaking failed, we found two tied tunings. Let the first one be our anchor tuning, <math>𝒈_0</math>, and the second be our <math>𝒈_1</math>: | |||
<math> | |||
𝒈_0 = \left[ \begin{array} {r} 399.809 & 1901.289 \end{array} \right] \\ | |||
<math> | 𝒈_1 = \left[ \begin{array} {r} 400.865 & 1903.401 \end{array} \right] \\ | ||
𝒈_0 = \left[ \begin{array} {r} 399.809 & 1901.289 \end{array} \right] \\ | </math> | ||
𝒈_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: | |||
Let me drop the cross-section diagram here again, for conveniently close reference, and with some smarter stuff on top of it this time: | |||
[[File:Blending augmented - RTT.png|frameless|800x800px]] | |||
[[File:Blending augmented - RTT.png|frameless|800x800px]] | |||
So our prediction looks like it should be about <math>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. | |||
So our prediction looks like it should be about <math>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>K</math>, again, for conveniently close reference: | ||
And here's the formula again for a tuning here from <math>K</math>, again, for conveniently close reference: | |||
<math> | |||
B = \mathrm{T}WK(DM\mathrm{T}WK)^{-1} | |||
<math> | </math> | ||
B = \mathrm{T}WK(DM\mathrm{T}WK)^{-1} | |||
</math> | |||
Let's get easy stuff out of the way first. We know <math>M</math> = {{rket|{{map|3 0 7}} {{map|0 1 0}}}}. As for <math>\mathrm{T}</math>, in the beginning we gave it as <math>\{ \frac32, \frac52, \frac53, \frac83, \frac95, \frac{16}{5}, \frac{15}{8}, \frac{18}{5} \}</math>. And <math>W</math> will just be the diagonal matrix of their log-product complexities, since we went with minimax-C tuning here. | |||
Let's get easy stuff out of the way first. We know <math>M</math> = {{rket|{{map|3 0 7}} {{map|0 1 0}}}}. As for <math>\mathrm{T}</math>, in the beginning we gave it as <math>\{ \frac32, \frac52, \frac53, \frac83, \frac95, \frac{16}{5}, \frac{15}{8}, \frac{18}{5} \}</math>. And <math>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>K</math> next then. Since <math>τ = 2</math> here—we have two tied tunings—we know <math>K</math> will have only <math>τ - 1 = 1</math> column. And <math>k = 8</math>, so that's its row count. In particular, we're looking for the tuning where <math>\frac95</math> and <math>\frac{16}{5}</math> have exactly equal errors, i.e. they even have the same sign, not opposite signs<ref group="note">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>𝒈</math>, <math>M</math>, and the ties</ref>. 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>\frac95</math> at index 5 of <math>\mathrm{T}</math> and <math>\frac{16}{5}</math> at index 6, we find <math>K</math> = [0 0 0 0 1 -1 0 0]. | ||
Okay, how about <math>K</math> next then. Since <math>τ = 2</math> | Now for <math>D</math>. We know it's a <math>(τ - 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>r</math> long like any generator tuning map. In our case we have <math>τ = 2</math> and <math>r = 2</math>, so it's a <math>(1,2)</math>-shaped matrix. That one row is <math>𝒈_1 - 𝒈_0</math>. So it's {{rbra|400.865 1903.401}} - {{rbra|399.809 1901.289}} = {{rbra|1.056 2.112}}. | ||
Now for <math>D</math>. We know it's a <math>(τ - 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>r</math> long like any generator tuning map. In our case we have <math>τ = 2</math> and <math>r = 2</math>, so it's a <math>(1,2)</math>-shaped matrix. That one row is <math>𝒈_1 - 𝒈_0</math>. So it's {{rbra|400.865 1903.401}} - {{rbra|399.809 1901.289}} = {{rbra|1.056 2.112}}. | And that's everything we need to solve! | ||
And that's everything we need to solve! | Work that out and we get <math>B</math> = [{{vector|-0.497891 -0.216260 -0.016343}}]. (We're showing a little extra precision here than usual.) So we can recover <math>𝒈</math> now as: | ||
Work that out and we get <math>B</math> = [{{vector|-0.497891 -0.216260 -0.016343}}]. (We're showing a little extra precision here than usual.) So we can recover <math>𝒈</math> now as: | |||
<math> | |||
𝒈 = 𝒈_0 + 𝒓_{0}BD | |||
<math> | </math> | ||
𝒈 = 𝒈_0 + 𝒓_{0}BD | |||
</math> | |||
We haven't worked out <math>𝒓_0</math> yet, but it's <math>𝒕_0 - 𝒋</math>, where <math>𝒋</math> = {{map|1200.000 1901.955 2786.314}} and <math>𝒕_0 = 𝒈_{0}M</math> = {{rbra|399.809 1901.289}}{{rket|{{map|3 0 7}} {{map|0 1 0}}}} = {{map|1199.43 1901.29 2798.66}}, so <math>𝒓_0</math> = {{map|0.573 0.666 12.349}}. | |||
We haven't worked out <math>𝒓_0</math> yet, but it's <math>𝒕_0 - 𝒋</math>, where <math>𝒋</math> = {{map|1200.000 1901.955 2786.314}} and <math>𝒕_0 = 𝒈_{0}M</math> = {{rbra|399.809 1901.289}}{{rket|{{map|3 0 7}} {{map|0 1 0}}}} = {{map|1199.43 1901.29 2798.66}}, so <math>𝒓_0</math> = {{map|0.573 0.666 12.349}}. | Plugging everything in at once could be unwieldy. So let's just do the <math>𝒓_{0}B</math> part to find <math>𝒃</math>. We might be curious about that anyway… how close does it match our prediction of about 0.35? Well, it's {{map|0.573 0.666 12.349}}[{{vector|-0.497891 -0.216260 -0.016343}}] = 0.343. Not bad! | ||
Plugging everything in at once could be unwieldy. So let's just do the <math>𝒓_{0}B</math> part to find <math>𝒃</math>. We might be curious about that anyway… how close does it match our prediction of about 0.35? Well, it's {{map|0.573 0.666 12.349}}[{{vector|-0.497891 -0.216260 -0.016343}}] = 0.343. Not bad! | So now plug <math>𝒃</math> into <math>𝒈 = 𝒈_0 + 𝒃D</math> and we find <math>𝒈</math> = {{rbra|399.809 1901.289}} + 0.343×{{rbra|1.056 2.112}} = {{rbra|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>\frac95</math> and <math>\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>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>𝒈</math> which are blends of other <math>𝒈</math>'s. But we need to look for <math>G</math>'s that are blends of other <math>G</math>'s! Doing that directly would explode the dimensionality of the space we're searching, by the rank <math>r</math> times the length of the blend vector <math>𝒃</math>, that is, <math>𝑟×(τ - 1)</math>. And would it even be meaningful to independently search the powers of the primes that comprise each entry of a <math>𝒈</math>? Probably not. The compressed information in <math>𝒈</math> is all that really matters for defining the constrained search region. So what if instead we still search by <math>𝒈</math>, but what if the blend we find for each <math>K</math> can be applied to <math>G</math>'s instead of <math>𝒈</math>'s? | |||
Let's test on an example. | |||
<math> | |||
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>𝜹_1 = 𝒈_1 - 𝒈_0</math> = {{rbra|-7.169 -1.792}}. Suppose we get <math>𝒃</math> = [0.5]. We know that <math>𝒃D</math> = {{rbra|-3.584 -0.896}}. So <math>𝒈</math> should be <math>𝒈_0 + 𝒃D</math> = {{rbra|1200 696.578}} + {{rbra|-3.584 -0.896}} = {{rbra|1196.416 695.682}}. | |||
But do we find the same tuning with <math>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>G_1 - G_0 =</math> | |||
<math> | |||
\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>b_1(G_1 - G_0)</math>, or half of that, is: | |||
<math> | |||
\left[ \begin{array} {r} | |||
\frac23 & \frac16 \\ | |||
-\frac23 & -\frac16 \\ | |||
\frac16 & \frac{1}{24} \\ | |||
\end{array} \right] | |||
</math> | |||
And add that to <math>G_0</math> then: | |||
<math> | |||
\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>\textbf{g}_1</math> here, the first column, {{vector|<math>\frac53</math> <math>-\frac23</math> <math>\frac16</math>}}, is <math>2^{\frac53}3^{-\frac23}5^{\frac16} \approx 1.996</math>. So <math>g_1</math> = 1196.416 ¢. | |||
And <math>\textbf{g}_2</math> here, the second column, {{vector|<math>\frac16</math> <math>-\frac16</math> <math>\frac{9}{24}</math>⟩, is <math>2^{\frac16}3^{-\frac16}5^{\frac{9}{24}} \approx 1.495</math>. So <math>g_2</math> = 695.682 ¢. | |||
Perfect! We wanted {{rbra|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".<ref group="note">https://yahootuninggroupsultimatebackup.github.io/tuning-math/topicId_20405.html#20412</ref> 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 [[Generator_embedding_optimization#Basic_tie-breaking_example:_setup|here]]: with Keenan's design, the intersection between <math>\frac51</math> and <math>\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>K</math> which make a target-interval itself unchanged, but it only does that when the <math>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>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>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>\frac21</math> is pure, and <math>\frac61</math> is pure, that implies that <math>\frac31</math> is pure. But if <math>\frac21</math> is pure, and <math>\frac32</math> is pure, that just as well implies that <math>\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 ([[#Within_target-interval_set|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 [[Generator_embedding_optimization#With_held-intervals_3|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 [[Generator_embedding_optimization#Blends:_abstract_concept|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>\mathbf{A}</math> and <math>\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 [[Generator_embedding_optimization#Apply_formula_to_example|this section]]. Note how the damage graphs for <math>\frac52</math> and <math>\frac{16}{5}</math> intersect (within this cross-section) but ''just outside the range'' where <math>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>\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>K</math> where every entry of <math>𝒃</math> is non-negative and <math>\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> | |||
\mathbf{P} = x\mathbf{A} + y\mathbf{B} + z\mathbf{C} | |||
</math> | |||
Add <math>\mathbf{A} - \mathbf{A}</math> to this, which changes nothing. | |||
<math> | |||
\mathbf{P} = \mathbf{A} - \mathbf{A} + x\mathbf{A} + y\mathbf{B} + z\mathbf{C} | |||
</math> | |||
Recognize a coefficient of <math>1</math> on the subtracted <math>\mathbf{A}</math>. | |||
<math> | |||
\mathbf{P} = \mathbf{A} - 1\mathbf{A} + x\mathbf{A} + y\mathbf{B} + z\mathbf{C} | |||
</math> | |||
We know <math>x + y + z = 1</math>, so we can substitute that in for this <math>1</math>. | |||
<math> | |||
\mathbf{P} = \mathbf{A} - (x + y + z)\mathbf{A} + x\mathbf{A} + y\mathbf{B} + z\mathbf{C} | |||
</math> | |||
Distribute. | |||
<math> | |||
\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>x</math> and <math>y</math>. | |||
<math> | |||
\mathbf{P} = \mathbf{A} + x(\mathbf{A} - \mathbf{A}) + y(\mathbf{B} - \mathbf{A}) + z(\mathbf{C} - \mathbf{A}) | |||
</math> | |||
Cancel out these <math>\mathbf{A}</math>'s and thus <math>x</math>. | |||
<math> | |||
\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> | |||
\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 [[Dave Keenan & Douglas Blumeyer's guide to RTT/Tuning computation#Tie_breaking:_power_limit_method|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!)"<ref group="note">https://yahootuninggroupsultimatebackup.github.io/tuning-math/topicId_20405.html#20412</ref>. 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>𝒈</math> by solving linear systems of equations to Dave Keenan's technique of computing exact generator embeddings <math>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>𝒈_0</math> = {{rbra|240.000 2786.314}} | |||
* <math>𝒈_1</math> = {{rbra|240.000 2795.336}} | |||
* <math>𝒈_2</math> = {{rbra|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>\mathbf{A}</math>, <math>\mathbf{B}</math>, and <math>\mathbf{C}</math> together to get <math>\mathbf{P}</math>, because <math>\mathbf{B}</math> and <math>\mathbf{C}</math> pull us away from <math>\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>𝒃</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>𝜹_i</math> are not only on the same line, but simple multiples of each other: | |||
* <math>𝜹_2 = 𝒈_1 - 𝒈_0</math> = {{rbra|240.000 2795.336}} - {{rbra|240.000 2786.314}} = {{rbra|0 9.0225}} | |||
* <math>𝜹_1 = 𝒈_2 - 𝒈_0</math> = {{rbra|240.000 2804.359}} - {{rbra|240.000 2786.314}} = {{rbra|0 18.045}} | |||
which is to say that <math>𝒈_1</math> happens to be ''smack-dab halfway'' between <math>𝒈_0</math> and <math>𝒈_1</math>. But that's just a distraction; that's not important. It could have been <math>ϕ</math> of the way between them instead and the problem would have been the same. | |||
Remember that these <math>𝜹_i</math> get combined into one big <math>D</math> matrix. In this case, that's | |||
<math> | |||
\left[ \begin{array} {r} | |||
0 & 9.0225 \\ | |||
0 & 18.045 \\ | |||
\end{array} \right] | |||
</math> | |||
Using this for <math>D</math>, however, causes every <math>B</math> we try to find via | |||
<math> | |||
B = \mathrm{T}WK(DM\mathrm{T}WK)^{-1} | |||
</math> | |||
to fail, because the <math>DM\mathrm{T}WK</math> matrix we try to invert is singular. (And for one reason or another, Keenan's way, using a <code>LinearSolve[]</code>, handled this degenerate case without complaining.) | |||
One might think the solution would be simply to canonicalize this <math>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>18.045 = 9.0225 × 2</math> we should be able to reduce this thing to: | |||
<math> | |||
\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 <code>Normalize[]</code> every <math>𝜹_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>0 < b_1 < 1</math>, but from <math>0 < b_1 < 9.0225</math>. | |||
== For all-interval tuning schemes == | |||
When computing an all-interval tuning where the dual norm power is <math>∞</math>, we use a variation on the method we used for ordinary tunings when the optimization power was <math>∞</math>. | When computing an all-interval tuning where the dual norm power is <math>∞</math>, we use a variation on the method we used for ordinary tunings when the optimization power was <math>∞</math>. | ||
In this case, our optimization power is also still <math>∞</math>. That is to say, that in this case we're doing the same computation we would have been doing if we had a finite target set, but now we're doing it as if the primes alone were our target set. | In this case, our optimization power is also still <math>∞</math>. That is to say, that in this case we're doing the same computation we would have been doing if we had a finite target-interval set, but now we're doing it as if the primes alone were our target-interval set. | ||
Let's get the minimax-S tuning of meantone. With three proxy target-intervals and two generators, we end up with four constraint matrices: | Let's get the minimax-S tuning of meantone. With three proxy target-intervals and two generators, we end up with four constraint matrices: | ||
Line 8,199: | Line 8,381: | ||
And so our second tuning wins, and that's our minimax-S tuning of meantone. | And so our second tuning wins, and that's our minimax-S tuning of meantone. | ||
==With alternative complexities== | == With alternative complexities == | ||
The following examples all pick up from a shared setup here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Alternative complexities#Computing all-interval tuning schemes with alternative complexities]]. | |||
The following examples all pick up from a shared setup here: [[Dave Keenan & Douglas Blumeyer's guide to RTT | |||
So for all complexities used | So for all complexities used here—at least the first several simpler examples—our constraint matrices will be: | ||
Line 8,238: | Line 8,419: | ||
===Minimax-S=== | === Minimax-S === | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Alternative complexities#Log-product2]], by plugging <math>L^{-1}</math> into our pseudoinverse method for <math>S_{\text{p}}</math>. | |||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT | |||
<math> | <math> | ||
Line 8,411: | Line 8,591: | ||
And so the minimax-S tuning of this temperament is {{rbra|1196.906 162.318}}. We could compute this in the RTT Library in Wolfram Language with the following line of code: | And so the minimax-S tuning of this temperament is {{rbra|1196.906 162.318}}. We could compute this in the RTT Library in Wolfram Language with the following line of code: | ||
<pre> | |||
In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-S"] | In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-S"] | ||
Out: {1196.906 162.318] </ | Out: {1196.906 162.318] </pre> | ||
===Minimax-sofpr-S=== | === Minimax-sofpr-S === | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Alternative complexities#Sum-of-prime-factors-with-repetition2]]. Plugging <math>\text{diag}(𝒑)^{-1}</math> in for <math>S_{\text{p}}</math>. | |||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT | |||
Now we need to find the tunings corresponding to our series of constraint matrices <math>K</math>. Those constraint matrices apply to both sides of the approximation <math> GM\mathrm{T}_{\text{p}}S_{\text{p}} \approx \mathrm{T}_{\text{p}}S_{\text{p}}</math>, or simplified, <math> GMS_{\text{p}} \approx S_{\text{p}}</math>. So first we find <math>MS_{\text{p}} = M\text{diag}(𝒑)^{-1} = </math> {{rket|{{bra|<math>\frac{1}{2}</math> <math>\frac{2}{3}</math> <math>\frac{3}{5}</math> }} {{bra|<math>\frac{0}{2}</math> <math>\frac{-3}{3}</math> <math>\frac{-5}{5}</math> }} }}. And then we find <math>S_{\text{p}} = \text{diag}(𝒑)^{-1} = \text{diag}(\left[ \begin{array} {r} \frac12 & \frac13 & \frac15 \end{array} \right])</math>. | Now we need to find the tunings corresponding to our series of constraint matrices <math>K</math>. Those constraint matrices apply to both sides of the approximation <math> GM\mathrm{T}_{\text{p}}S_{\text{p}} \approx \mathrm{T}_{\text{p}}S_{\text{p}}</math>, or simplified, <math> GMS_{\text{p}} \approx S_{\text{p}}</math>. So first we find <math>MS_{\text{p}} = M\text{diag}(𝒑)^{-1} = </math> {{rket|{{bra|<math>\frac{1}{2}</math> <math>\frac{2}{3}</math> <math>\frac{3}{5}</math> }} {{bra|<math>\frac{0}{2}</math> <math>\frac{-3}{3}</math> <math>\frac{-5}{5}</math> }} }}. And then we find <math>S_{\text{p}} = \text{diag}(𝒑)^{-1} = \text{diag}(\left[ \begin{array} {r} \frac12 & \frac13 & \frac15 \end{array} \right])</math>. | ||
Line 8,578: | Line 8,757: | ||
And so the minimax-sopfr-S tuning of this temperament is {{rbra|1196.927 162.430}}. We could compute this in the RTT Library in Wolfram Language with the following line of code: | And so the minimax-sopfr-S tuning of this temperament is {{rbra|1196.927 162.430}}. We could compute this in the RTT Library in Wolfram Language with the following line of code: | ||
<pre> | |||
In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-sopfr-S"] | In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-sopfr-S"] | ||
Out: {1196.927 162.430] </ | Out: {1196.927 162.430] </pre> | ||
===Minimax-copfr-S=== | === Minimax-copfr-S === | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Alternative complexities#Count-of-prime-factors-with-repetition2]]. Plugging <math>I</math> into our pseudoinverse method for <math>S_{\text{p}}</math>. | |||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT | |||
Now we need to find the tunings corresponding to our series of constraint matrices <math>K</math>. Those constraint matrices apply to both sides of the approximation <math> GM\mathrm{T}_{\text{p}}S_{\text{p}} \approx \mathrm{T}_{\text{p}}S_{\text{p}}</math>, or simplified, <math> GMS_{\text{p}} \approx S_{\text{p}}</math>. So first we find <math>MS_{\text{p}} = M = </math> {{rket|{{map|1 2 3}} {{map|0 -3 -5}} }}. And then we find <math>S_{\text{p}} = I</math>. | Now we need to find the tunings corresponding to our series of constraint matrices <math>K</math>. Those constraint matrices apply to both sides of the approximation <math> GM\mathrm{T}_{\text{p}}S_{\text{p}} \approx \mathrm{T}_{\text{p}}S_{\text{p}}</math>, or simplified, <math> GMS_{\text{p}} \approx S_{\text{p}}</math>. So first we find <math>MS_{\text{p}} = M = </math> {{rket|{{map|1 2 3}} {{map|0 -3 -5}} }}. And then we find <math>S_{\text{p}} = I</math>. | ||
Line 8,747: | Line 8,925: | ||
And so the minimax-copfr-S tuning of this temperament is {{rbra|1194.537 160.552}}. We could compute this in the RTT Library in Wolfram Language with the following line of code: | And so the minimax-copfr-S tuning of this temperament is {{rbra|1194.537 160.552}}. We could compute this in the RTT Library in Wolfram Language with the following line of code: | ||
<pre> | |||
In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-copfr-S"] | In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-copfr-S"] | ||
Out: {1194.537 160.552] </ | Out: {1194.537 160.552] </pre> | ||
In the case of minimax-copfr-S with nullity-1 (only one comma) like this, we actually have a shortcut. First, take the size of the comma in cents, and divide it by its total count of primes. The porcupine comma is <math>\frac{250}{243}</math>, or in vector form {{vector|1 -5 3}}, and so it has |1| + |-5| + |3| = 9 total primes. And being 49.166 ¢ in size, that gives us <math>\frac{49.166}{9} = 5.463</math>. What's this number for? That's the amount of cents to retune each prime by! If the count of a prime in the comma is ''positive'', we tune ''narrow'' by that much, and if ''negative'', we tune ''wide''. So the map for the minimax-copfr-S tuning of porcupine is <math>𝒕</math> = {{map|1200 1901.955 2786.314}} + {{map|-5.463 5.463 -5.463}} = {{map|1194.537 1907.418 2780.851}}. If you're not convinced this matches the <math>𝒈</math> we found the long way, feel free to check via <math>𝒕 = 𝒈M</math>. | In the case of minimax-copfr-S with nullity-1 (only one comma) like this, we actually have a shortcut. First, take the size of the comma in cents, and divide it by its total count of primes. The porcupine comma is <math>\frac{250}{243}</math>, or in vector form {{vector|1 -5 3}}, and so it has |1| + |-5| + |3| = 9 total primes. And being 49.166 ¢ in size, that gives us <math>\frac{49.166}{9} = 5.463</math>. What's this number for? That's the amount of cents to retune each prime by! If the count of a prime in the comma is ''positive'', we tune ''narrow'' by that much, and if ''negative'', we tune ''wide''. So the map for the minimax-copfr-S tuning of porcupine is <math>𝒕</math> = {{map|1200 1901.955 2786.314}} + {{map|-5.463 5.463 -5.463}} = {{map|1194.537 1907.418 2780.851}}. If you're not convinced this matches the <math>𝒈</math> we found the long way, feel free to check via <math>𝒕 = 𝒈M</math>. | ||
===Minimax-lils-S=== | === Minimax-lils-S === | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Alternative complexities#Log-integer-limit-squared2]]. | |||
Now we need to find the tunings corresponding to our series of constraint matrices <math>K</math>. Those constraint matrices apply to both sides of the approximation <math> GM\mathrm{T}_{\text{p}}S_{\text{p}} \approx \mathrm{T}_{\text{p}}S_{\text{p}}</math>, or simplified, <math> GMS_{\text{p}} \approx S_{\text{p}}</math>, or equivalent thereof. So first we find <math>M\mathrm{T}_{\text{p}}S_{\text{p}}</math>. According to Mike's augmentation pattern<ref group="note">https://yahootuninggroupsultimatebackup.github.io/tuning-math/topicId_21029.html</ref>, we get: | |||
Now we need to find the tunings corresponding to our series of constraint matrices <math>K</math>. Those constraint matrices apply to both sides of the approximation <math> GM\mathrm{T}_{\text{p}}S_{\text{p}} \approx \mathrm{T}_{\text{p}}S_{\text{p}}</math>, or simplified, <math> GMS_{\text{p}} \approx S_{\text{p}}</math>, or equivalent thereof. So first we find <math>M\mathrm{T}_{\text{p}}S_{\text{p}}</math>. According to Mike's augmentation pattern<ref>https://yahootuninggroupsultimatebackup.github.io/tuning-math/topicId_21029.html</ref>, we get: | |||
Line 9,046: | Line 9,223: | ||
The best tuning we find from this set is {{rbra|1193.828 161.900}}, and so that's our minimax-lils-S tuning of porcupine. We could compute this in the RTT Library in Wolfram Language with the following line of code: | The best tuning we find from this set is {{rbra|1193.828 161.900}}, and so that's our minimax-lils-S tuning of porcupine. We could compute this in the RTT Library in Wolfram Language with the following line of code: | ||
<pre> | |||
In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-lils-S"] | In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "minimax-lils-S"] | ||
Out: {1193.828 161.900] </ | Out: {1193.828 161.900] </pre> | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT | === Minimax-lols-S === | ||
This example specifically picks up from the setup laid out here: [[Dave Keenan & Douglas Blumeyer's guide to RTT/Alternative complexities#Log-odd-limit-squared2]]. | |||
So for minimax-lols-S (AKA held-octave minimax-lils-S) we basically keep the same <math>MS_{\text{p}}</math> as before. But now (as discussed [[Generator_embedding_optimization#With held-intervals3|here]]) we have to further augment it with the mapped held-interval, {{vector|1 0 0}} (i.e. what the octave maps to in this temperament, including its augmented row, so that we can match it with its just size in the constrained linear system of equations to enforce it being held unchanged): | So for minimax-lols-S (AKA held-octave minimax-lils-S) we basically keep the same <math>MS_{\text{p}}</math> as before. But now (as discussed [[Generator_embedding_optimization#With held-intervals3|here]]) we have to further augment it with the mapped held-interval, {{vector|1 0 0}} (i.e. what the octave maps to in this temperament, including its augmented row, so that we can match it with its just size in the constrained linear system of equations to enforce it being held unchanged): | ||
Line 9,315: | Line 9,491: | ||
We could compute this in the RTT Library in Wolfram Language with the following line of code: | We could compute this in the RTT Library in Wolfram Language with the following line of code: | ||
<pre> | |||
In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "held-octave minimax-lols-S"] | In: optimizeGeneratorTuningMap["[⟨1 2 3] ⟨0 -3 -5]]", "held-octave minimax-lols-S"] | ||
Out: {1200 162.737] </ | Out: {1200 162.737] </pre> | ||
=Footnotes= | = Footnotes = | ||
<references group="note" /> | |||
[[Category:Regular temperament theory]] | [[Category:Regular temperament theory]] | ||
[[Category:Tuning]] | [[Category:Tuning]] |