Module:Limits: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Plumtree (talk | contribs)
m Temporarily reverting the edit
Tag: Undo
Plumtree (talk | contribs)
Consistency limits optimisation
Line 1: Line 1:
local rat = require('Module:Rational')
local rat = require('Module:Rational')
local p = {}
local p = {}
-- multiply the ratio by a power of `equave` so that it lies within [1; equave)
function p.canonical(a, equave)
equave = equave or 2
if type(a) == 'number' then
a = rat.new(a)
end
local b = rat.copy(a)
while rat.lt(b, 1) do
b = rat.mul(b, equave)
end
while rat.geq(b, equave) do
b = rat.div(b, equave)
end
return b
end


-- compute all positive ratios n/m with n and m <= q modulo powers of equave
-- compute all positive ratios n/m with n and m <= q modulo powers of equave
Line 25: Line 9:
for m = 1, q, 2 do
for m = 1, q, 2 do
local a = rat.new(n, m)
local a = rat.new(n, m)
a = p.canonical(a, equave)
a = rat.modulo_mul(a, equave)
local key = rat.as_ratio(a)
local key = rat.as_ratio(a)
ratios[key] = a
ratios[key] = a
Line 44: Line 28:
local approx_set = {}
local approx_set = {}
for a_key, a in pairs(ratios) do
for a_key, a in pairs(ratios) do
local a_approx = approximate(a)
local a_approx = approximate(a) % size
if approx_set[a_approx] then
if approx_set[a_approx] then
return false
return false
Line 64: Line 48:
local c_approx = approximate(c)
local c_approx = approximate(c)
c = p.canonical(c, equave)
c = rat.modulo_mul(c, equave)
local c_key = rat.as_ratio(c)
local c_key = rat.as_ratio(c)
if ratios[c_key] then
if ratios[c_key] then

Revision as of 12:15, 3 October 2022

Module documentation[view] [edit] [history] [purge]
Todo: add documentation

local rat = require('Module:Rational')
local p = {}

-- compute all positive ratios n/m with n and m <= q modulo powers of equave
function p.limit_modulo_equave(q, equave)
	equave = equave or 2
	local ratios = {}
	for n = 1, q, 2 do
		for m = 1, q, 2 do
			local a = rat.new(n, m)
			a = rat.modulo_mul(a, equave)
			local key = rat.as_ratio(a)
			ratios[key] = a
		end
	end
	return ratios
end

-- check additive consistency for a set of ratios (modulo powers of equave):
--   approx(a*b) = approx(a) + approx(b) forall a, b: a, b, ab in ratios
-- `distinct`: whether distinct ratios are required to be mapped to distinct approximations
function p.additively_consistent(equave, size, ratios, distinct)
	distinct = distinct or false
	local function approximate(a)
		return math.floor(size * math.log(rat.as_float(a)) / math.log(rat.as_float(equave)) + 0.5)
	end
	if distinct then
		local approx_set = {}
		for a_key, a in pairs(ratios) do
			local a_approx = approximate(a) % size
			if approx_set[a_approx] then
				return false
			end
			approx_set[a_approx] = true
		end
	end
	local ratios_ordered = {}
	for a_key, a in pairs(ratios) do
		table.insert(ratios_ordered, a)
	end
	for i, a in ipairs(ratios_ordered) do
		local a_approx = approximate(a)
		for j, b in ipairs(ratios_ordered) do
			if i <= j then
				local b_approx = approximate(b)
				
				local c = rat.mul(a, b)
				local c_approx = approximate(c)
				
				c = rat.modulo_mul(c, equave)
				local c_key = rat.as_ratio(c)
				if ratios[c_key] then
					if c_approx ~= a_approx + b_approx then
						return false
					end
				end
			end
		end
	end
	return true
end

-- find additive consistency limit
-- returns nil when at least `max_n`
-- `distinct`: whether distinct ratios are required to be mapped to distinct approximations
function p.consistency_limit(size, equave, distinct, max_n)
	max_n = max_n or 1/0
	equave = equave or 2
	distinct = distinct or false
	local n = 1
	local last_n = 1
	while true do
		if rat.is_int(rat.div(n, equave)) then
			n = n + 1
		else
			local ratios = p.limit_modulo_equave(n, equave)
			local consistent = p.additively_consistent(equave, size, ratios, distinct)
			if not consistent then
				return last_n
			end
			last_n = n
			n = n + 1
		end
		if n > max_n then
			return nil
		end
	end
end

return p