Constrained tuning: Difference between revisions
Expansion |
Update the script and style of the tuning maps |
||
| Line 44: | Line 44: | ||
Otherwise, as a standard optimization problem, numerous algorithms exist to solve it, such as [[Wikipedia: Sequential quadratic programming|sequential quadratic programming]], to name one. | Otherwise, as a standard optimization problem, numerous algorithms exist to solve it, such as [[Wikipedia: Sequential quadratic programming|sequential quadratic programming]], to name one. | ||
The following [https://www.python.org Python] code is an excerpt from [[Flora Canou]]'s [https://github.com/FloraCanou/te_temperament_measures/blob/ | The following [https://www.python.org Python] code is an excerpt from [[Flora Canou]]'s [https://github.com/FloraCanou/te_temperament_measures/blob/545cc301418253cf46785c6c59a0cc6754986b08/tuning_optimizer.py tuning optimizer]. Note: it depends on [https://scipy.org/ Scipy]. | ||
{{Databox| Code | | {{Databox| Code | | ||
<syntaxhighlight lang="python"> | <syntaxhighlight lang="python"> | ||
# © 2020-2021 Flora Canou | Version 0. | # © 2020-2021 Flora Canou | Version 0.10 | ||
# This work is licensed under the GNU General Public License version 3. | # This work is licensed under the GNU General Public License version 3. | ||
| Line 58: | Line 58: | ||
SCALAR = 1200 #could be in octave, but for precision reason | SCALAR = 1200 #could be in octave, but for precision reason | ||
def weighted (matrix, subgroup, | def weighted (matrix, subgroup, wtype = "tenney"): | ||
if not | if not wtype in {"tenney", "frobenius", "partch"}: | ||
type | wtype = "tenney" | ||
raise Warning ("unknown weighter type, using default (\"tenney\")") | |||
if | if wtype == "tenney": | ||
weighter = np.diag (1/np.log2 (subgroup)) | weighter = np.diag (1/np.log2 (subgroup)) | ||
elif | elif wtype == "frobenius": | ||
weighter = np.eye (len (subgroup)) | weighter = np.eye (len (subgroup)) | ||
elif wtype == "partch": | |||
weighter = np.diag (np.log2 (subgroup)) | |||
return matrix @ weighter | return matrix @ weighter | ||
| Line 71: | Line 74: | ||
return linalg.norm (gen @ map - jip, ord = order) | return linalg.norm (gen @ map - jip, ord = order) | ||
def optimizer_main (map, subgroup = | def optimizer_main (map, subgroup = None, wtype = "tenney", order = 2, cons_monzo_list = None, stretch_monzo = None, show = True): | ||
if | if subgroup is None: | ||
subgroup = PRIME_LIST[:map.shape[1]] | subgroup = PRIME_LIST[:map.shape[1]] | ||
jip = np.log2 (subgroup)*SCALAR | jip = np.log2 (subgroup)*SCALAR | ||
map_w = weighted (map, subgroup, | map_w = weighted (map, subgroup, wtype = wtype) | ||
jip_w = weighted (jip, subgroup, | jip_w = weighted (jip, subgroup, wtype = wtype) | ||
if order == 2 and | if order == 2 and cons_monzo_list is None: #te with no constraints, simply use lstsq for better performance | ||
res = linalg.lstsq (map_w.T, jip_w) | res = linalg.lstsq (map_w.T, jip_w) | ||
gen = res[0] | gen = res[0] | ||
| Line 84: | Line 87: | ||
else: | else: | ||
gen0 = [SCALAR]*map.shape[0] #initial guess | gen0 = [SCALAR]*map.shape[0] #initial guess | ||
cons = {'type': 'eq', 'fun': lambda gen: (gen @ map - jip) @ cons_monzo_list} | cons = () if cons_monzo_list is None else {'type': 'eq', 'fun': lambda gen: (gen @ map - jip) @ cons_monzo_list} | ||
res = optimize.minimize (error, gen0, args = (map_w, jip_w, order), method = "SLSQP", constraints = cons) | res = optimize.minimize (error, gen0, args = (map_w, jip_w, order), method = "SLSQP", constraints = cons) | ||
print (res.message) | print (res.message) | ||
| Line 90: | Line 93: | ||
gen = res.x | gen = res.x | ||
if stretch_monzo | if not stretch_monzo is None: | ||
gen *= (jip @ stretch_monzo)/(gen @ map @ stretch_monzo) | gen *= (jip @ stretch_monzo)/(gen @ map @ stretch_monzo) | ||
| Line 97: | Line 100: | ||
return gen | return gen | ||
optimiser_main = optimizer_main | |||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 106: | Line 111: | ||
The pure-octave CTE tuning can be very different from [[POTE tuning]]. Take 7-limit meantone as an example, the POTE [[tuning map]]: | The pure-octave CTE tuning can be very different from [[POTE tuning]]. Take 7-limit meantone as an example, the POTE [[tuning map]]: | ||
{ | <math>\langle \begin{matrix} 1200.000 & 1896.495 & 2785.980 & 3364.949 \end{matrix} ]</math> | ||
This is a little bit flatter than [[quarter-comma meantone]], with all the primes tuned flat. | This is a little bit flatter than [[quarter-comma meantone]], with all the primes tuned flat. | ||
| Line 112: | Line 117: | ||
The pure-octave CTE tuning map: | The pure-octave CTE tuning map: | ||
{ | <math>\langle \begin{matrix} 1200.000 & 1896.952 & 2787.809 & 3369.521 \end{matrix} ]</math> | ||
This is a little bit sharper than quarter-comma meantone, with prime 3 tuned flat and 5 and 7 sharp. | This is a little bit sharper than quarter-comma meantone, with prime 3 tuned flat and 5 and 7 sharp. | ||