perf: split the harmonic coefficient matrix into its independent blocks - #82
Merged
Merged
Conversation
`rearrange!` solves the linear system relating the harmonic equations to the derivatives of the harmonic variables. For an n-harmonic ansatz that matrix is n uncoupled 2x2 blocks, but it was eliminated whole. A Bareiss entry carries the determinant of everything eliminated so far, so by pivot k the polynomial arithmetic runs on k blocks' worth of determinant and every solution comes out over the determinant of the full matrix instead of the single block determinant that survives. `fraction_free_linear_solve` now finds the connected components of the bipartite row-column graph (union-find over the structural nonzeros) and eliminates each on its own. An unbalanced component is structurally singular and is handed to the full solve to reject, so nothing that used to throw now silently succeeds. Over six representative systems `rearrange_standard` drops from 134.2 MiB to 10.1 MiB and from 0.114 s to 0.028 s. The worst case, two coupled oscillators at two harmonics, goes from 80.8 MiB to 3.1 MiB. The solutions are deliberately not byte-identical to before: each now carries only its own block's determinant, so it is smaller. Verified by substituting them back into the source equations at random numeric points; the largest residual across all six systems is 9.4e-15. Alongside this: - Sums and products are rebuilt with SymbolicUtils' n-ary `add_worker`/ `mul_worker` rather than folded with `sum`/`prod`/`+=`, which is O(n^2) in the number of terms. A solved Bareiss entry has thousands of monomials. - `simplify_exp_products` missed `exp(a)*exp(a)`: a product stores a repeated factor as a power, so it arrives as the single factor `exp(a)^2` and the `isexp` test skipped it. - `collapse_pythagorean` committed to the first squared trig factor it found in a summand, leaving `cos(3t)^2*sin(t)^2*a + sin(3t)^2*sin(t)^2*a` uncollapsed. Every way of splitting a summand is now a candidate partner. - `is_rearranged` asked whether a derivative appears on the left-hand side by rendering both sides to strings and calling `occursin`. It now walks the tree. - `trig_reduce` called `expand_all` where `expand` suffices: the extra `Postwalk(expand_exp_power)` was the single most expensive step of the averaging, and the `simplify_exp_products` on the next line already normalises `exp(a)^n` at every node it reaches. - `declare_variable` built its variable with `@eval` and bound the result inside QuestBase. Nothing read that binding, and evaluating into a closed module makes the package impossible to precompile with a workload.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
rearrange!solves the linear system relating the harmonic equations to the derivatives of the harmonic variables. For an n-harmonic ansatz that matrix is n uncoupled 2x2 blocks, but it was eliminated whole. A Bareiss entry carries the determinant of everything eliminated so far, so by pivot k the polynomial arithmetic runs on k blocks' worth of determinant, and every solution comes out over the determinant of the full matrix instead of the single block determinant that survives.fraction_free_linear_solvenow finds the connected components of the bipartite row-column graph (union-find over the structural nonzeros) and eliminates each on its own. An unbalanced component is structurally singular and is handed to the full solve to reject, so nothing that used to throw now silently succeeds.Over six representative systems
rearrange_standarddrops from 134.2 MiB to 10.1 MiB and from 0.114 s to 0.028 s. The worst case, two coupled oscillators at two harmonics, goes from 80.8 MiB to 3.1 MiB.The solutions are deliberately not byte-identical to before: each now carries only its own block's determinant, so it is smaller. Verified by substituting them back into the source equations at random numeric points; the largest residual across all six systems is 9.4e-15.
Alongside this:
add_worker/mul_workerrather than folded withsum/prod/+=, which is quadratic in the number of terms. A solved Bareiss entry has thousands of monomials.simplify_exp_productsmissedexp(a)*exp(a): a product stores a repeated factor as a power, so it arrives as the single factorexp(a)^2and theisexptest skipped it.collapse_pythagoreancommitted to the first squared trig factor it found in a summand, leavingcos(3t)^2*sin(t)^2*a + sin(3t)^2*sin(t)^2*auncollapsed. Every way of splitting a summand is now a candidate partner.is_rearrangedasked whether a derivative appears on the left-hand side by rendering both sides to strings and callingoccursin. It now walks the tree.trig_reducecalledexpand_allwhereexpandsuffices: the extraPostwalk(expand_exp_power)was the single most expensive step of the averaging, and thesimplify_exp_productson the next line already normalisesexp(a)^nat every node it reaches.declare_variablebuilt its variable with@evaland bound the result inside QuestBase. Nothing read that binding, and evaluating into a closed module makes the package impossible to precompile with a workload.