Skip to content

feat: use the native solver of LoopStructural when it is installed - #148

Merged
lachlangrose merged 4 commits into
mainfrom
feat/native-solver
Nov 1, 2026
Merged

lachlangrose merged 4 commits into
mainfrom
feat/native-solver

Conversation

@lachlangrose

Copy link
Copy Markdown
Member

This PR is stacked on #145, so the base is feat/viewer-object-registry.

What changes

  • New plugin setting Solver in the plugin options: "Native (fast, if it is installed)" (the default) or "Default (Python)".
  • With Native, and when loop-interpolation-core is installed, every interpolator gets solver='native' (through interpolator_arguments). Without the core, the plugin uses the default solver, as before.
  • The native solver also solves the inequality constraints, so the plugin does not set ADMM for them when it uses native.
  • A feature that the native solver cannot solve (for example a fold interpolator or a region mask) is built again with the default solver: cg, or ADMM for inequalities. The plugin uses the default solver for that feature from then on, and writes one log message (_solve_feature in model_manager.py).
  • New module main/solver_choice.py (no QGIS import).

Needs a LoopStructural release

The native solver is only in loop-interpolation 0.2, and LoopStructural up to 1.10.0 requires loop-interpolation<0.1.0. Loop3D/LoopStructural#381 relaxes the pin and adds loopstructural[native]. After that release (1.11.0), change the requirement of the plugin to LoopStructural[native]~=1.11.0. Until then this PR changes nothing for the users: the core is not installed, so the default solver is used.

Tests

  • New unit tests (tests/unit/test_solver_choice.py) and QGIS tests (tests/qgis/test_native_solver.py) for the choice of the solver and the fallback.
  • tests/unit: 343 passed. tests/qgis (run locally with the Python of QGIS 3.42): 244 passed.
  • In a clean environment with LoopStructural from #381 and the PyPI packages: a 50 000-element foliation solves in 2.5 s with native and 10.6 s with cg (results within 0.1 %). The fallback (native refused, then cg) works for a foliation and for a fault frame.

🤖 Generated with Claude Code

@lachlangrose
lachlangrose added this pull request to stack #154 November 1, 2026 06:49
Base automatically changed from feat/viewer-object-registry to main November 1, 2026 06:51
- New setting Solver: Native (the default) or Default. With Native, and
  when loop-interpolation-core is installed, the interpolators get
  solver='native'. Without the core, the plugin uses the default solver.
- The native solver also solves the inequality constraints, so ADMM is
  not set for them when native is used.
- A feature that the native solver cannot solve (for example a fold or a
  region mask) is built again with the default solver (cg, or ADMM for
  inequalities). The plugin uses the default solver for that feature from
  then on, and writes one log message.
- LoopStructural 1.11.0 allows loop-interpolation 0.2, which has the native
  solver. Require LoopStructural[native]~=1.11.0 in the three requirement
  files.
- The pin bump workflow read the key "LoopStructural" from the release
  manifest, but the key of the root package is ".", so it always stopped.
  It also did not update loopstructural/requirements.txt (the file that
  qpip reads) and did not match a pin with an extra.
…eature

The coordinates 1 and 2 of a fault frame have orthogonal rows that the
native core cannot read, so all the faults used cg. Now the plugin finds
the coordinates that the core refuses (with the support check of
loop-interpolation), solves only those with the default solver, and keeps
native for the others. It records the coordinates, so the next build does
not try native for them again. If the check finds no coordinate, the full
feature uses the default solver, as before.

The tests for the ADMM solver of inequality rows now turn the native core
off, because with it the native solver solves the inequalities.
…when it does not converge

The native core stops after 5 000 iterations by default; cg allows 10 x the
number of nodes. On a large grid the native solve stopped before it
converged. loop-interpolation then keeps the old field, which had the size
of the old grid, so the surfaces failed ("cannot assign 51000 values to
array of shape 87232").

- Before each solve, give each native builder maxiter = 10 x its number of
  nodes.
- After the build, a coordinate whose native solve did not converge is
  solved again with the default solver, and uses it from then on. The
  native limit is not given to the default solver.
@lachlangrose
lachlangrose merged commit a74e867 into main Nov 1, 2026
5 of 6 checks passed
@lachlangrose
lachlangrose deleted the feat/native-solver branch November 1, 2026 06:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant