Constrained tuning: Difference between revisions

Mike Battaglia (talk | contribs)
Add lots of info about POTE, KE, CTWE, etc
Mike Battaglia (talk | contribs)
fast simple CTE algorithm
Line 182: Line 182:


which is almost an analytical solution. Notice we introduced the vector of lagrange multipliers ''Λ'', with length equal to the number of constraints. The lagrange multipliers have no concrete meaning for the resulting tuning, so they can be discarded.
which is almost an analytical solution. Notice we introduced the vector of lagrange multipliers ''Λ'', with length equal to the number of constraints. The lagrange multipliers have no concrete meaning for the resulting tuning, so they can be discarded.
== Simple Fast Algorithm ==
A much simpler way to compute the CTE tuning is just to note that it's what we get if we modify the TE tuning so that the weighting of the 2's coefficient is very large. As the weighting goes to infinity, we get the CTE tuning. Thus, we can set it to some sufficiently large number, so that we get whatever numerical precision we want, and compute the result in closed-form using the pseudoinverse. The result is only a few lines of numpy code:
import numpy as np
def modified_TE(limit, M, H=1):
    """
    Computes the modified TE/CTE tuning of a *full-limit* temperament given
    a limit and mapping matrix H.
    This function interpolates between the regular TE and the CTE: for H = 0 we
    have the TE, and as H -> infinity we have the CTE.
    To compute the CTE to any precision, set H to some sufficiently large value.
    """
    # Compute adjusted TE weighting matrix and JIP
    W = np.diag(1/np.log2(limit))
    W[0,0] = H
    J = 1200*np.log2(limit)
    # Main calculation: get the generator map g such that g@M@W ≈ J@W. Use pinv
    G = (J@W) @ np.linalg.pinv(M@W)
    T = G @ M
    return G, T
# %% Compute the CTE of Sensi temperament
H = 1_000_000
limit = np.array([2, 3, 5, 7])
M = np.array( # mapping matrix for Sensi
    [[1, -1, -1, -2],
      [0,  7,  9, 13]]
)
G, T = modified_TE(limit, M, H)
print("Generator map: " + str(G))
print("Tuning map: " + str(T))


== CTE tuning vs POTE tuning CWE tuning vs CTWE tuning ==
== CTE tuning vs POTE tuning CWE tuning vs CTWE tuning ==
Line 215: Line 255:
The "good-enough" solution suggested on the tuning list is to instead just choose an '''idealized''' 2's coordinate, which is a real number, which instead makes the span equal to zero, which ought to be good enough. This is equivalent to placing the entire span, or rather its negation, into the 2's coordinate. This happens to be the same thing as just using the Weil-norm, which can be thought of as the L1 norm in an "augmented space" where we add the span as an extra coordinate. Regardless of if we remove factors of 2 and add a coordinate for the span, or put the span in the 2's coordinate, we clearly get the same thing.
The "good-enough" solution suggested on the tuning list is to instead just choose an '''idealized''' 2's coordinate, which is a real number, which instead makes the span equal to zero, which ought to be good enough. This is equivalent to placing the entire span, or rather its negation, into the 2's coordinate. This happens to be the same thing as just using the Weil-norm, which can be thought of as the L1 norm in an "augmented space" where we add the span as an extra coordinate. Regardless of if we remove factors of 2 and add a coordinate for the span, or put the span in the 2's coordinate, we clearly get the same thing.


=== CTWE tuning ==
=== CTWE tuning ===


As mentioned above, if we constrain the equave to be pure, and look for the tuning map that is closest to the JIP using the WE norm, we get the CWE tuning, a.k.a. KE tuning.
As mentioned above, if we constrain the equave to be pure, and look for the tuning map that is closest to the JIP using the WE norm, we get the CWE tuning, a.k.a. KE tuning.