Error measures for DR chords: Difference between revisions
No edit summary |
→Fully DR, rooted linear: Correct analytical solution for x |
||
| (14 intermediate revisions by 2 users not shown) | |||
| Line 3: | Line 3: | ||
== Conventions and introduction == | == Conventions and introduction == | ||
The idea motivating least-squares error measures on a chord as an approximation to a given delta signature is the following | The idea motivating least-squares error measures on a chord as an approximation to a given delta signature is the following: | ||
We want the error of a chord 1:''r''<sub>1</sub>:''r''<sub>2</sub>:...:''r''<sub>''n''</sub> (in increasing order), with ''n'' > 1, in the linear domain as an approximation to a | We want the error of a chord {{nowrap|1:''r''<sub>1</sub>:''r''<sub>2</sub>:...:''r''<sub>''n''</sub>}} (in increasing order, we also take {{nowrap|''r''<sub>0</sub> {{=}} 1}}), with {{nowrap|''n'' > 1}}, in the linear domain as an approximation to a delta-rational chord with signature {{nowrap|+δ<sub>1</sub> +δ<sub>2</sub> ... +δ<sub>''n''</sub>}} (possibly with some deltas free), i.e. a chord | ||
<math>x : x + \delta_1 : \cdots : x + \sum_{l=1}^n \delta_l</math> | <math>x : x + \delta_1 : \cdots : x + \sum_{l=1}^n \delta_l</math> | ||
with root real-valued harmonic ''x''. Let <math>D_0 = 0, D_i = \sum_{k=1}^i \delta_k</math> be the delta signature +δ<sub>1</sub> +δ<sub>2</sub> ... +δ<sub>''n''</sub> written cumulatively. | with root real-valued harmonic ''x''. Let <math>D_0 = 0, D_i = \sum_{k=1}^i \delta_k</math> be the delta signature {{nowrap|+δ<sub>1</sub> +δ<sub>2</sub> ... +δ<sub>''n''</sub>}} written cumulatively. | ||
We want to measure the error ''without having to fix any dyad'' (as one might naively fix a dyad and measure errors in the other deltas in relation to the fixed dyad). To do this we solve a least-squares | We want to measure the error ''without having to fix any dyad in the target chord'' (as one might naively fix a dyad and measure errors in the other deltas in relation to the fixed dyad). To do this we solve a least-squares optimization problem: use a root-sum-square objective function and optimize ''x'' (and any free deltas) to minimize that function. | ||
== Domain and error model == | == Domain and error model == | ||
| Line 21: | Line 21: | ||
** ''All-steps'': Only intervals between adjacent notes are compared. | ** ''All-steps'': Only intervals between adjacent notes are compared. | ||
We arrive at the following general formula: Let <math>[n] = \{0, 1, 2, ..., n\},</math> let <math>I \subseteq {[n] \choose 2}</math> be the error model, and let <math>f_D</math> represent the domain function (identity for linear, or <math>\log</math>). Then the error | We arrive at the following general formula: Let <math>[n] = \{0, 1, 2, ..., n\},</math> let <math>I \subseteq {[n] \choose 2}</math> be the error model, and let <math>f_D</math> represent the domain function (identity for linear, or <math>\log</math>). Then the objective function (measuring the error) to be minimized by optimizing <math>x</math> and any free deltas is: | ||
<math> | <math> | ||
| Line 28: | Line 28: | ||
{| class="wikitable" | {| class="wikitable" | ||
|+ | |+ style="font-size: 105%;" | Objective function for various modes and error models | ||
|- | |- | ||
! | ! Domain | ||
! | ! Error model | ||
! | ! Objective function | ||
|- | |- | ||
!rowspan="3"|Linear | ! rowspan="3" | Linear | ||
! | ! Rooted | ||
| <math>\sqrt{\sum_{i=1}^n \Bigg( \frac{x + D_i}{x} - r_i \Bigg)^2 } = \sqrt{\sum_{i=1}^n \Bigg( 1 + \frac{D_i}{x} - r_i \Bigg)^2 }</math> | |||
|- | |- | ||
! | ! Pairwise | ||
| <math>\sqrt{\sum_{0\leq i<j\leq n} \Bigg( \frac{x + D_j}{x + D_i} - \frac{r_j}{r_i} \Bigg)^2 }</math> | |||
|- | |- | ||
! | ! All-steps | ||
| <math>\sqrt{\sum_{0\leq i<n} \Bigg( \frac{x + D_{i+1}}{x + D_i} - \frac{r_{i+1}}{r_i} \Bigg)^2 }</math> | |||
|- | |- | ||
!rowspan="3"|Logarithmic<br/>(nepers) | ! rowspan="3" | Logarithmic<br />(nepers) | ||
! | ! Rooted | ||
| <math>\sqrt{\sum_{i=1}^n \Bigg(\log \frac{x + D_i}{r_i x} \Bigg)^2}</math> | |||
|- | |- | ||
! | ! Pairwise | ||
| <math>\sqrt{\sum_{0\leq i<j\leq n} \Bigg(\log \frac{x + D_j}{x + D_i} - \log \frac{r_j}{r_i} \Bigg)^2}</math> | |||
|- | |- | ||
! | ! All-steps | ||
| <math>\sqrt{\sum_{0\leq i<n} \Bigg(\log \frac{x + D_{i+1}}{x + D_i} - \log \frac{r_{i+1}}{r_i} \Bigg)^2}</math> | |||
|} | |} | ||
To convert nepers to cents, multiply by <math>\frac{1200}{\log 2}.</math> | To convert nepers to cents, multiply by <math>\frac{1200}{\log 2}.</math> | ||
== Solution methods == | == Solution methods == | ||
This section gets into the depths of mathematical optimization methods used to minimize DR error. (Optimization is a whole field and there are many different methods; the reason | This section gets into the depths of mathematical optimization methods used to minimize DR error. (Optimization is a whole field and there are many different methods; the reason most of those methods exist is to find solutions when finding them symbolically is infeasible.) | ||
=== Analytic === | === Analytic === | ||
==== Fully DR, rooted linear ==== | ==== Fully DR, rooted linear ==== | ||
Setting the derivative to 0 gives us the closed-form solution | Setting the derivative to 0 gives us the closed-form solution | ||
<math>x = \frac{\sum_{i=1}^n D_i }{ | <math>x = \frac{\sum_{i=1}^n D_i^2 }{\sum_{i=1}^n D_i(r_i-1)},</math> | ||
which can be plugged back into | which can be plugged back into | ||
<math>\sqrt{\sum_{ | <math>\sqrt{\sum_{i=1}^n \Bigg( 1 + \frac{D_i}{x} - r_i \Bigg)^2 }</math> | ||
to obtain the least-squares linear error. | to obtain the least-squares linear error. | ||
| Line 79: | Line 81: | ||
where ''y'' represents the free delta +?. | where ''y'' represents the free delta +?. | ||
We can set the partial derivatives with respect to ''x'' and ''y'' of the inner expression equal to zero (since the derivative of sqrt() is never 0) and use SymPy to solve the system symbolically: | We can set the partial derivatives with respect to ''x'' and ''y'' of the inner expression equal to zero (since the derivative of <code>sqrt()</code> is never 0) and use SymPy to solve the system symbolically: | ||
<syntaxhighlight lang="py"> | <syntaxhighlight lang="py"> | ||
| Line 98: | Line 100: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
The unique solution with ''x'' | The unique solution with {{nowrap|''x'' > 0}} is | ||
<math> | <math> | ||
| Line 149: | Line 151: | ||
return GridSolution(best_x, best_error) | return GridSolution(best_x, best_error) | ||
<syntaxhighlight> | </syntaxhighlight> | ||
=== BFGS-B (one related delta set, arbitrary free deltas) === | === BFGS-B (one related delta set, arbitrary free deltas) === | ||
We let ''x''<sub>1</sub> = ''x'' and include additional free variables ''x''<sub>2</sub>, ..., ''x''<sub>n</sub>, one for every additional +?, after coalescing strings of consecutive +?'s into one +? and after trimming leading and trailing free delta segments. | We let {{nowrap|''x''<sub>1</sub> {{=}} ''x''}} and include additional free variables ''x''<sub>2</sub>, ..., ''x''<sub>''n''</sub>, one for every additional +?, after coalescing strings of consecutive +?'s into one +? and after trimming leading and trailing free delta segments. | ||
BFGS-B is a quasi-Newton optimization method (based on BFGS) particularly suited for this problem: | BFGS-B is a quasi-Newton optimization method (based on BFGS) particularly suited for this problem: | ||
* The | * The objective function is smooth, allowing use of gradients | ||
* Fast convergence, requiring at worst 20 iterations for accuracy | * Fast convergence, requiring at worst 20 iterations for accuracy | ||
* Naturally deals with the ''x'' | * Naturally deals with the {{nowrap|''x'' > 0}} constraint using a log barrier and minimizing the transformed function using the unconstrained BFGS method | ||
* Acceptable memory usage given a realistic number of parameters for practical DR chords (up to 3 interior free delta segments, thus 4 parameters). | * Acceptable memory usage given a realistic number of parameters for practical DR chords (up to 3 interior free delta segments, thus 4 parameters). | ||
It is a ''quasi-Newton method'' because it uses an approximation of the Hessian (matrix of mixed second partial derivatives) of the | It is a ''quasi-Newton method'' because it uses an approximation of the Hessian (matrix of mixed second partial derivatives) of the objective function at each step. | ||
In the Python implementation below, <code>x</code> represents the vector <math>(x_1, x_2, ..., x_n),</math> <code>x0</code> is the initial guess for the solution, and <code>f</code> is the | In the Python implementation below, <code>x</code> represents the vector <math>(x_1, x_2, ..., x_n),</math> <code>x0</code> is the initial guess for the solution, and <code>f</code> is the objective function. | ||
<syntaxhighlight lang="python"> | <syntaxhighlight lang="python"> | ||
| Line 298: | Line 300: | ||
result.fx = f(result.x) | result.fx = f(result.x) | ||
return result | return result | ||
<syntaxhighlight> | </syntaxhighlight> | ||
=== L-BFGS-B (one related delta set, arbitrary free deltas) === | === L-BFGS-B (one related delta set, arbitrary free deltas) === | ||
| Line 410: | Line 412: | ||
return LBFGSSolution(x, fx, max_iterations, False) | return LBFGSSolution(x, fx, max_iterations, False) | ||
<syntaxhighlight> | </syntaxhighlight> | ||
== External links == | == External links == | ||
* [ | * [http://turbofishcrow.github.io/delta/ Inthar's DR chord explorer (includes least-squares error calculation in both domains and multiple error models)] | ||
[[Category:Math]] | [[Category:Math]] | ||