Constrained tuning: Difference between revisions
Fix the wrong definition that went unnoticed for months |
Update the code |
||
| Line 23: | Line 23: | ||
As a standard optimization problem, numerous algorithms exist to solve for this tuning, such as [[Wikipedia: Sequential quadratic programming|sequential quadratic programming]], to name one. | As a standard optimization problem, numerous algorithms exist to solve for this tuning, 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/54c9ec58acf0ad5adc7c1d96f2deaf1d2a503702/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.8 | ||
# 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 39: | Line 37: | ||
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, type = "tenney"): | ||
if not type in {"tenney", "frobenius"}: | |||
return matrix @ | type = "tenney" | ||
if type == "tenney": | |||
weighter = np.diag (1/np.log2 (subgroup)) | |||
elif type == "frobenius": | |||
weighter = np.eye (len (subgroup)) | |||
return matrix @ weighter | |||
def error (gen, map, jip, order = 2): | def error (gen, map, jip, order = 2): | ||
return linalg.norm (gen @ map - jip, ord = order) | return linalg.norm (gen @ map - jip, ord = order) | ||
def optimizer_main (map, subgroup = [], order = 2, cons_monzo_list = np.array ([]), stretch_monzo = np.array ([]), show = True): | def optimizer_main (map, subgroup = [], order = 2, weighter = "tenney", cons_monzo_list = np.array ([]), stretch_monzo = np.array ([]), show = True): | ||
if len (subgroup) == 0: | if len (subgroup) == 0: | ||
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, type = weighter) | ||
jip_w = weighted (jip, subgroup) | jip_w = weighted (jip, subgroup, type = weighter) | ||
if order == 2 and not cons_monzo_list.size: #te with no constraints, simply use lstsq for better performance | if order == 2 and not cons_monzo_list.size: #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] | ||
print (" | print ("L2 tuning without constraints, solved using lstsq. ") | ||
else: | else: | ||
gen0 = [SCALAR]*map.shape[0] #initial guess | gen0 = [SCALAR]*map.shape[0] #initial guess | ||
cons = {'type': 'eq', 'fun': lambda gen: (gen @ | cons = {'type': 'eq', 'fun': lambda gen: (gen @ map - jip) @ cons_monzo_list} if cons_monzo_list.size else () | ||
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) | ||