Module:JI ratios: Difference between revisions
m m.member_name -> med.member_name; use standalone int-limit search function |
Rename/reorganize functions; add prime-limit search by reusing subgroup-search code |
||
| Line 6: | Line 6: | ||
-- TODO: | -- TODO: | ||
-- | -- Replace old int-limit search function with new one | ||
-- Template for handling multiple entry of JI ratios into a template, and for | -- Template for handling multiple entry of JI ratios into a template, and for | ||
| Line 15: | Line 13: | ||
-- JI ratios are searched by the following params in a hierarchy: | -- JI ratios are searched by the following params in a hierarchy: | ||
-- - | -- - Search by prime limit. Int limit is used to limit the num/den of ratios. | ||
-- | -- Prime limit takes precedence over subgroup. | ||
-- - | -- - Search by subgroup. (Subgroup may contain nonprime numbers, but ratios are | ||
-- | -- currently not supported.) Int limit is used to limit the num/den of ratios. | ||
-- - If neither prime limit or subgroup is present, search by prime limit. This | |||
-- is considered the absolute minimum requirement for ratio searching. | |||
-- - If prime limit is present, | |||
-- | |||
-- NOTES: | -- NOTES: | ||
-- - Prime limits are infinite sets, so int limit is used to restrain the set | -- - Prime limits are infinite sets, so int limit is used to restrain the set | ||
-- to a finite size. The same is true for subgroup. | -- to a finite size. The same is true for subgroup. | ||
-- - Tenney height is used for further filtering of ratios, and is considered | -- - Tenney height is used for further filtering of ratios, and is considered | ||
-- optional. | -- optional. If omitted, tenney height defaults to infinity. | ||
-- INT_LIMIT_MAX is hardcoded to limit the size of output. | -- INT_LIMIT_MAX is hardcoded to limit the size of output. This only applies to | ||
-- int limit search, as other search functions (subgroup, prime-limit) may allow | |||
-- higher search maxima. For reference, searching within the octave yields this | |||
-- many ratios: | |||
-- 400 -> ~24000 ratios | -- 400 -> ~24000 ratios | ||
-- 300 -> ~14000 ratios | -- 300 -> ~14000 ratios | ||
| Line 44: | Line 43: | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
-- | -- Function to be replaced with new one | ||
function p.search_by_int_limit(integer_limit, max_cents) | function p.search_by_int_limit(integer_limit, max_cents) | ||
local max_cents = max_cents or 1200 | local max_cents = max_cents or 1200 | ||
| Line 92: | Line 89: | ||
end | end | ||
---- | -- Int-limit-based search; finds ratios between 1/1 and an equave, within an int | ||
-- | -- limit. An optional tenney height can be passed in. | ||
-- | -- Int limit is hardcoded to a max size to restrict the size of output, to avoid | ||
-- risk of out-of-memory operations or the like. | |||
-- | function p.search_by_int_limit_within_equave(int_limit, equave, tenney_height) | ||
- | local int_limit = int_limit or DEFAULT_INT_LIMIT | ||
local equave = equave or rat.new(2,1) -- Defualt equave is 2/1. | |||
function p. | local tenney_height = tenney_height or 1/0 -- Defualt tenney height is infinity. | ||
local int_limit = int_limit or | int_limit = math.max(0, math.min(INT_LIMIT_MAX, int_limit)) | ||
local equave = equave or | |||
local | local init_ratios = {{1,1}, {2,1}} | ||
local ratios = | local search_func = p.int_limit_mediant_search | ||
local search_args = { ["equave"] = equave, ["int_limit"] = int_limit, ["tenney_height"] = tenney_height } | |||
local ratios = med.find_only_mediants_by_search_func(init_ratios, search_func, search_args) | |||
-- Convert to ratios that Module:Rational can work with | -- Convert to ratios that Module:Rational can work with | ||
| Line 115: | Line 113: | ||
end | end | ||
-- | -- Int limit search function, with equave and tenney height cutoffs. | ||
-- | -- If nil is passed in for the tenney height, it will defualt to infinity. | ||
function p. | -- To be passed into mediant-search function, as part of int-limit-search | ||
local | -- function call. | ||
local | function p.int_limit_mediant_search(mediant_data, search_args) | ||
local mediant = mediant_data["mediant"] | |||
local ratio_1 = mediant_data["ratio_1"] | |||
local equave = search_args["equave"] | |||
local int_limit = search_args["int_limit"] | |||
local tenney_height = search_args["tenney_height"] | |||
local equave_as_float = rat.as_float(equave) | |||
local rat_1_as_float = ratio_1[1] / ratio_1[2] | |||
local mediant_th = math.log(mediant[1] * mediant[2]) / math.log(2) | |||
-- | return math.max(mediant[1], mediant[2]) <= int_limit and rat_1_as_float < equave_as_float and mediant_th <= tenney_height | ||
end | |||
-------------------------------------------------------------------------------- | |||
------------------------ SUBGROUP-BASED SEARCH FUNCTION ------------------------ | |||
-------------------------------------------------------------------------------- | |||
-- Int-limit-based search; finds ratios between 1/1 and an equave, within a sub- | |||
-- group. An int limit is passed in to limit the size of output, since subgroups | |||
-- are infinite sets. An optional tenney height can be passed in to further | |||
-- | -- limit output. | ||
-- | -- Unlike int limit search, subgroup search can allow for very high int limits, | ||
-- | -- as long as the subgroup is reasonably small and has reasonably small terms. | ||
-- | -- Note that members in a subgroup need not be prime, as long as the terms are, | ||
-- for the most part, relatively prime. | |||
function p.search_by_subgroup_within_equave(subgroup, int_limit, equave, tenney_height) | |||
local subgroup = subgroup or { 2, 3, 7 } | |||
local int_limit = int_limit or 50 | |||
local equave = equave or rat.new(2,1) -- Defualt equave is 2/1. | |||
local tenney_height = tenney_height or 1/0 -- Defualt tenney height is infinity. | |||
-- Be absolutely sure the subgroup's members are sorted! | |||
table.sort(subgroup) | |||
-- Find all possible products given the factors in the subgroup. | |||
-- These will be used to find all possible ratios. | |||
local products = {{1}} | local products = {{1}} | ||
local new_products_found = true | local new_products_found = true | ||
while new_products_found do | while new_products_found do | ||
local new_products = {} | local new_products = {} | ||
for i = 1, # | for i = 1, #subgroup do | ||
for j = 1, #products[#products] do | for j = 1, #products[#products] do | ||
local new_product = products[#products][j] * | local new_product = products[#products][j] * subgroup[i] | ||
if new_product <= | if new_product <= int_limit then | ||
local product_already_added = false | local product_already_added = false | ||
for k = 1, #new_products do | for k = 1, #new_products do | ||
| Line 170: | Line 189: | ||
products = consolidated_products | products = consolidated_products | ||
table.sort(products) | table.sort(products) | ||
-- | -- Using the products produced earlier, combine them to make all possible | ||
- | -- ratios from 1/1 to the equave. Ratios with non-coprime numerator and | ||
-- | -- denominator, or exceed the tenney height, are omitted. | ||
local ratios = {} | local ratios = {} | ||
for i = 1, # | local equave_as_float = rat.as_float(equave) | ||
local denominator = | for i = 1, #products do | ||
for j = i, # | local denominator = products[i] | ||
local numerator = | for j = i, #products do | ||
local numerator = products[j] | |||
local gcd = utils._gcd(numerator, denominator) | local gcd = utils._gcd(numerator, denominator) | ||
if gcd == 1 then | if gcd == 1 then | ||
local within_equave = numerator / denominator <= equave_as_float | local within_equave = numerator / denominator <= equave_as_float | ||
if within_equave then | local within_tenney_height = math.log(numerator * denominator) / math.log(2) <= tenney_height | ||
if within_equave and within_tenney_height then | |||
table.insert(ratios, {numerator, denominator}) | table.insert(ratios, {numerator, denominator}) | ||
else | else | ||
| Line 198: | Line 210: | ||
end | end | ||
end | end | ||
end | |||
-- Convert to ratios that Module:Rational can work with | |||
for i = 1, #ratios do | |||
ratios[i] = rat.new(ratios[i][1], ratios[i][2]) | |||
end | end | ||
| Line 203: | Line 220: | ||
end | end | ||
-------------------------------------------------------------------------------- | |||
---------------------- PRIME-LIMIT-BASED SEARCH FUNCTION ----------------------- | |||
-------------------------------------------------------------------------------- | |||
-- Int-limit-based search; finds ratios between 1/1 and an equave, within a | |||
-- prime limit. An int limit is passed in to limit the size of output, since | |||
-- prime limits are inifinite sets. An optional tenney height can be passed in | |||
-- to further limit output. | |||
-- Like subgroup search, prime limit search can also allow for very high int | |||
-- limits, as long as the prime is reasonably small. | |||
function p.search_by_prime_limit_within_equave(prime_limit, int_limit, equave, tenney_height) | |||
local prime_limit = 3 | |||
local int_limit = int_limit or 1000 | |||
local equave = equave or rat.new(2,1) -- Defualt equave is 2/1. | |||
local tenney_height = tenney_height or 1/0 -- Defualt tenney height is infinity. | |||
-- Find all primes up to the prime limit. | |||
local primes = {} | |||
for i = 2, prime_limit do | |||
local is_prime = true | |||
for j = 2, math.floor(math.sqrt(i)) do | |||
if i % j == 0 then | |||
is_prime = false | |||
break | |||
end | |||
end | |||
if is_prime then | |||
table.insert(primes, i) | |||
end | |||
end | |||
-- Perform subgroup search on the primes found, as subgroup-search code can | |||
-- be reused for prime-limit search. | |||
return p.search_by_subgroup_within_equave(primes, int_limit, equave, tenney_height) | |||
end | |||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
| Line 371: | Line 423: | ||
end | end | ||
-- Convert a table of tables into a | -- Convert a table of ratios (tables, as defined by rational module) into a | ||
-- line of text, with options for delimiters. | |||
function p.ratios_as_texts(ratios, add_links, delimiter) | function p.ratios_as_texts(ratios, add_links, delimiter) | ||
local add_links = add_links == true | local add_links = add_links == true | ||
| Line 384: | Line 437: | ||
end | end | ||
function p.tester() | |||
function p. | |||
local ratios = p.search_by_prime_limit_within_equave() | |||
return p.ratios_as_text(ratios) | return p.ratios_as_text(ratios) | ||