Godtone (talk | contribs)
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
Godtone (talk | contribs)
→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) * et2,combine=lambda badnesses: sum(badnesses),et2=0):
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]
if type(ivs)==int:
# finally, return the result:
ivs = [(p,1) for p in lim(ivs)]
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)
elif type(ivs)==list:
ivs = odd_lim(1,[],ivs)
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 * iv_complexity(x)),up_to)
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>