Godtone
Joined 17 December 2020
m →My Python 3 code: add strict_optimal_edo_sequence for only considering an edo better if it's better in terms of relative error |
→My Python 3 code: add more functionality to et_badness and explain the code, add more customisability to optimal_edo_sequence and strict_optimal_edo_sequence |
||
| Line 1,291: | Line 1,291: | ||
patent_vals = dict() | patent_vals = dict() | ||
ivs_cache = [] | |||
ivs_sum_weights_cache = 1 | |||
# the default badness is the sum of the squares of the errors with each interval's contribution weighted proportional to its odd-limit complexity, | # the default badness is the sum of the squares of the errors with each interval's contribution weighted proportional to its odd-limit complexity, | ||
# in terms of absolute (cent/octave) error (corresponding to the multiplication by et2). | # in terms of absolute (cent/octave) error (corresponding to the multiplication by et2) and divided by the sum of the weightings of the intervals. | ||
def et_badness(ivs,v,badness=lambda rel_err,x,et2: rel_err**2 * iv_complexity(x) | def et_badness(ivs,v,badness=0,weighting=0,combine=0,et2=0): | ||
# deduce if using default badness and if so note it for later | |||
default_badness = False | |||
if not badness: | |||
badness = lambda rel_err,x,et2: rel_err**2 * et2 | |||
default_badness = True | |||
# if the weighting is unspecified (=0), use the default of: | |||
# weighting an interval x proportional to its odd-limit complexity iv_complexity(x) | |||
if not weighting: | |||
weighting = lambda x: iv_complexity(x) | |||
elif weighting==1 or weighting in ['unweighted','trivial','basic','CE','CEE']: | |||
weighting = lambda x: 1 # for use with prime harmonic interval sets | |||
elif weighting==2 or weighting in ['natural', 'harmonic', 'TE', 'te', 'tenney']: | |||
weighting = lambda x: 1/math.log2(x[0]) # for use with prime harmonic interval sets | |||
# convert the ivs argument into a valid set of intervals before attempting operations with it | |||
if type(ivs)==int: | |||
ivs = [(p,1) for p in lim(ivs)] | |||
elif type(ivs)==list: | |||
ivs = odd_lim(1,[],ivs) | |||
# deduce of using the default behaviour (sum of badnesss) and if so note it for later | |||
using_sum = False | |||
global ivs_cache | |||
global ivs_sum_weights_cache | |||
if not combine: | |||
combine = lambda badnesses: sum(badnesses) | |||
using_sum = True | |||
# specifically, cache the sum of weighting(x), as set of intervals likely to remain constant between calls. | |||
# this is to allow meaningful comparison of performance between different sets of intervals, as otherwise | |||
# adding an interval to the set always increases the total badness even if the average per interval decreases, | |||
# so this is for automatically calculating the average w.r.t. the weighting function given. | |||
# it is thus redundant if you are not comparing between different sets of JI intervals as targets. | |||
if ivs_cache!=ivs: | |||
ivs_cache = ivs | |||
ivs_sum_weights_cache = sum([ weighting(x) for x in ivs ]) | |||
# use the cached val if it is present; otherwise, create a 251-prime-limit (255-odd-limit) patent val and cache it | |||
global patent_vals | global patent_vals | ||
if type(v)==int: | if type(v)==int: | ||
| Line 1,301: | Line 1,337: | ||
v = val(lim(255),ed(v)) | v = val(lim(255),ed(v)) | ||
patent_vals[v[0]] = v | patent_vals[v[0]] = v | ||
# deduce the number of arguments in the badness function to allow only using the first n arguments as needed | |||
num_args = badness.__code__.co_argcount | num_args = badness.__code__.co_argcount | ||
badness3args = lambda rel_err,x,et2: 0 | badness3args = lambda rel_err,x,et2: 0 | ||
| Line 1,309: | Line 1,346: | ||
elif num_args==3: | elif num_args==3: | ||
badness3args = lambda rel_err,x,et2: badness(rel_err,x,et2) | badness3args = lambda rel_err,x,et2: badness(rel_err,x,et2) | ||
# deduce the et2 parameter if not given; if it is given, it can be a tempered octave/tritave/etc. | |||
if not et2: | if not et2: | ||
et2 = 1/v[0] | et2 = 1/v[0] | ||
# finally, return the result: | |||
return combine([ badness3args(abs( map_iv(v,x) - steps(x,et2) ), x, et2) * weighting(x) for x in ivs ]) / (ivs_sum_weights_cache if using_sum else 1) | |||
return combine([ badness3args(abs( map_iv(v,x) - steps(x,et2) ), x, et2) for x in ivs ]) | |||
# if you just give a list or set of intervals (a,b), the default behaviour is to judge the badness of an edo as: | # if you just give a list or set of intervals (a,b), the default behaviour is to judge the badness of an edo as: | ||
# * sum of squares of errors with each interval's contribution weighted proportional to its odd-limit complexity, | # * sum of squares of errors with each interval's contribution weighted proportional to its odd-limit complexity, | ||
# in term of absolute error (cent/octave) error | # in term of absolute error (cent/octave) error, (redundantly) divided by the sum of the weightings of the intervals | ||
# * for edos in the 2 to 311 range (inclusive) | # * for edos in the 2 to 311 range (inclusive) | ||
def optimal_edo_sequence(edo_badness,up_to=311): | def optimal_edo_sequence(edo_badness,up_to=311,weighting=0,combine=0): | ||
if type(edo_badness)==list or type(edo_badness)==set: | if type(edo_badness)==list or type(edo_badness)==set: | ||
ivs = edo_badness | ivs = edo_badness | ||
edo_badness = lambda edo: et_badness(ivs,edo) | edo_badness = lambda edo: et_badness(ivs,edo,0,weighting,combine) | ||
best_edo = edo_badness(1) | best_edo = edo_badness(1) | ||
best_edos = [] | best_edos = [] | ||
| Line 1,335: | Line 1,370: | ||
# it only considers an edo better than a smaller edo if it does better in pure relative error. | # it only considers an edo better than a smaller edo if it does better in pure relative error. | ||
# this gives much sparser but also much more interesting lists. | # this gives much sparser but also much more interesting lists. | ||
def strict_optimal_edo_sequence(ivs,up_to=311): | # default weighting of an interval x is proportional to its odd-limit complexity iv_complexity(x). | ||
return optimal_edo_sequence(lambda edo: et_badness(ivs,edo,lambda rel_err,x,et2: rel_err**2 | def strict_optimal_edo_sequence(ivs,up_to=311,weighting=0,combine=0): | ||
return optimal_edo_sequence(lambda edo: et_badness(ivs,edo,lambda rel_err,x,et2: rel_err**2,weighting,combine),up_to) | |||
</syntaxhighlight> | </syntaxhighlight> | ||