Module:Temperament data: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
CompactStar (talk | contribs)
No edit summary
CompactStar (talk | contribs)
No edit summary
Line 263: Line 263:
result = result:sub(0,-3)
result = result:sub(0,-3)
if subgroup[1] ~= 2 then
if subgroup[1] ~= 2 then
result = result .. '\n\n[[Optimal tuning]]s:\n*[[POTE|Pure-' .. subgroup[1] .. '/1 TE]]'
result = result .. '\n\n[[Optimal tuning]]s:\n*[[POTE|Pure-' .. subgroup[1] .. '/1 TE]]: '
else
else
result = result .. '\n\n[[Optimal tuning]]s:\n*[[POTE]]: '
result = result .. '\n\n[[Optimal tuning]]s:\n*[[POTE]]: '

Revision as of 04:07, 17 March 2024

Module documentation[view] [edit] [history] [purge]
This module should not be invoked directly; use its corresponding template instead: Template:Temperament data.
Module:Temperament data is a draft module. It is incomplete and may not be in active development. If possible, editors are encouraged to help with its development. In the meantime, editors should avoid using this module across the Xenharmonic Wiki, except for testing.
Introspection summary for Module:Temperament data 
Functions provided (1)
Line Function Params
240 temperament_data (invokable) (frame)
Lua modules required (2)
Variable Module Functions used
rat Module:Rational as_pair
parse
u Module:Utils _round

No function descriptions were provided. The Lua code may have further information.


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

local function gcd(a,b)
  if type(a) == 'number' and type(b) == 'number' and 
        a == math.floor(a) and b == math.floor(b) then
    if b == 0 then
      return a
    else
      return gcd(b, a % b) -- tail recursion
    end
  else
    error('Invalid argument to gcd (' .. tostring(a) .. ',' .. 
          tostring(b) .. ')', 2)
  end
end

-- Linear algebra and RTT functions

local function matadd(a, b)
	local result = {}
	for i = 1, #a  do
		result[i] = {}
		for j = 1, #(b[1]) do
			result[i][j] = a[i][j] + b[i][j]
		end
	end
	return result
end

local function matsub(a, b)
	local result = {}
	for i = 1, #a  do
		result[i] = {}
		for j = 1, #(b[1]) do
			result[i][j] = a[i][j] - b[i][j]
		end
	end
	return result
end

local function matmul(a, b)
	local result = {}
	for i = 1, #a  do
		result[i] = {}
		for j = 1, #(b[1]) do
			result[i][j] = 0
			for k = 1, #(a[1]) do
				result[i][j] = result[i][j] + (a[i][k] * b[k][j])
			end
		end
	end
	return result
end


local function scalarmatmul(a, b)
	local result = {}
	for i = 1, #a  do
		result[i] = {}
		for j = 1, #(a[1]) do
			result[i][j] = a[i][j] * b
		end
	end
	return result
end

local function matinv(a)
	local dbl_identity = {}
	for i = 1, #a do
		dbl_identity[i] = {}
		for j = 1, #a do
			if i == j then
				dbl_identity[i][j] = 2
			else
				dbl_identity[i][j] = 0
			end
		end
	end

	local xn = scalarmatmul(a, 0.000005)
	
	for i = 1, 30 do
		xn = matmul(xn, matsub(dbl_identity, matmul(a, xn)))
	end
	return xn
end

local function transpose(a)
	local result = {}
	for i = 1, #a[1] do
		result[i] = {}
		for j = 1, #a do
			result[i][j] = a[j][i]
		end
	end
	return result
end

local function antitranspose(a)
	local result = {}
	for i = 1, #a[1] do
		result[i] = {}
		for j = 1, #a do
			result[i][j] = a[#a - j + 1][#a[1] - i + 1]
		end
	end
	return result
end


local function pseudoinv(a)
	return matmul(transpose(a), matinv(matmul(a, transpose(a))))
end

local function nullspace(mapping)
	local identity = {}
	for i = 1, #mapping[1] do
		identity[i] = {}
		for j = 1, #mapping[1] do
			if i == j then
				identity[i][j] = 1
			else
				identity[i][j] = 0
			end
		end
	end

	-- local w = {{0},{1},{0}}
	-- for i = 1, #mapping[1] do
	-- 	w[i] = {10}
	-- end

	return matsub(identity, matmul(pseudoinv(mapping), mapping))
end

local function unreduced_mapping_from_basis(comma_basis)
	return antitranspose(nullspace(antitranspose(comma_basis)))
end

local function get_te_tuning_map(subgroup, comma_basis)
	local v = unreduced_mapping_from_basis(comma_basis)
	local w = {}
	for i = 1, #subgroup do
		w[i] = {}
		for j = 1, #subgroup do
			if i == j then
				w[i][j] = math.log(2)/math.log(subgroup[i])
			else
				w[i][j] = 0
			end
		end
	end
	
	local jw = {{}}
	for i = 1, #subgroup do
		jw[1][i] = 1
	end
	local vw = matmul(v, w)
	local g = matmul(jw, pseudoinv(vw))
	return g
end

local function get_te_generator(subgroup, comma_basis, preimage)
	return matmul(get_te_tuning_map(subgroup, comma_basis), preimage)
end

local function get_pote_generator(subgroup, comma_basis, preimage)
	local period = 1
	for i = 1, #subgroup do
		period = period * (subgroup[i]^preimage[i][1])
	end
	mw.logObject(period)
	local te = get_te_generator(subgroup, comma_basis, preimage)
	local stretch_factor = te[1][1] * math.log(period) / math.log(2)
	return scalarmatmul(te, 1/ stretch_factor)
end

-- Parsing/display functions

local function int_to_subgroup_monzo(subgroup, x) 
	local result = {}
	local x2 = x
	for i = 1, #subgroup do
		result[i] = 0
		while true do
			x2 = x2 / subgroup[i]
			if x2 ~= math.floor(x2) then
				break
			end
			result[i] = result[i] + 1
		end
		x2 = x
	end
	
	return result
end


local function rat_to_subgroup_monzo(subgroup, x)
	local n, d = rat.as_pair(x)
	return matsub({int_to_subgroup_monzo(subgroup, n)}, {int_to_subgroup_monzo(subgroup, d)})[1]	
end

local function rat_list_to_matrix(subgroup, list)
	local result = {}
	for j = 1, #subgroup do
		result[j] = {}
	end
	
	for i = 1, #list do
		local smonzo = rat_to_subgroup_monzo(subgroup, list[i])
		for j = 1, #subgroup do
			result[j][i] = smonzo[j]
		end
	end
	
	return result
end

local function mysplit (inputstr, sep)
        if sep == nil then
                sep = '%s'
        end
        local t={}
        for str in string.gmatch(inputstr, '([^'..sep..']+)') do
                table.insert(t, str)
        end
        return t
end

local function trim(x)
	local str = x
	str = str:gsub('%s+', '')
    str = string.gsub(str, '%s+', '')
    return str
end

function p.temperament_data(frame)
	local subgroup = mysplit(frame.args['subgroup'], '.')
	for i = 1, #subgroup do
		subgroup[i] = tonumber(subgroup[i])
	end
	local comma_matrix = mysplit(frame.args['comma_list'], ',')
	for i = 1, #comma_matrix do
		comma_matrix[i] = rat.parse(comma_matrix[i])
	end
	comma_matrix = rat_list_to_matrix(subgroup, comma_matrix)
	local unparsed_gens = mysplit(frame.args['generators'], ',')
	local generators =  mysplit(frame.args['generators'], ',')
	for i = 1, #generators do
		generators[i] = rat.parse(generators[i])
	end
	generators = rat_list_to_matrix(subgroup, generators)
	local pote_generator = get_pote_generator(subgroup, comma_matrix, generators)
	local result =  '[[Subgroup]]: ' .. frame.args['subgroup']
	result = result .. '\n\n[[Comma list]]: ' .. frame.args['comma_list']
	result = result .. '\n\n: mapping generators: '
	for i = 1, #unparsed_gens do
		result = result .. '~' .. trim(unparsed_gens[i]) .. ', '
	end
	result = result:sub(0,-3)
	if subgroup[1] ~= 2 then
		result = result .. '\n\n[[Optimal tuning]]s:\n*[[POTE|Pure-' .. subgroup[1] .. '/1 TE]]: '
	else
		result = result .. '\n\n[[Optimal tuning]]s:\n*[[POTE]]: '
	end
	for i = 1, #(pote_generator[1]) do
		if pote_generator[1][i] == 1 then
			result = result .. '~' .. trim(unparsed_gens[i]) ..  ' = ' .. '1\\1' .. ', '
		else
			result = result .. '~' .. trim(unparsed_gens[i]) .. ' = ' .. u._round(pote_generator[1][i] * 1200, 7) .. ', '
		end
	end
	result = result:sub(0,-3)
	return result
end


return p