Skip to content

Generic LinearOperator.toarray() and tosparse() defaults - #89

Merged
max-models merged 4 commits into
devel-tinyfrom
generic-linop-toarray
Oct 2, 2026
Merged

max-models merged 4 commits into
devel-tinyfrom
generic-linop-toarray

Conversation

@spossann

@spossann spossann commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

Core changes:

  • LinearOperator.toarray(out=None, is_sparse=False, format='csr') is no longer abstract. The default builds the global matrix one column at a time, as dot(e_j), so it works for any operator, including matrix-free ones. It supports StencilVectorSpace domains and (possibly nested) BlockVectorSpace domains made of them. It runs in serial and in parallel (every rank gets the full matrix) and returns either a dense array or a scipy.sparse matrix. This is ported from struphy's LinOpWithTransp.toarray_struphy(), with these improvements:
    • nested blocks are supported, with one code path for Stencil and Block domains;
    • the dense result is summed across ranks in place (Allreduce(IN_PLACE)), so no second full-size array is allocated;
    • one ghost-region update per column instead of two;
    • the sparse result is collected with one allgather per array, and the format is converted with coo_matrix.asformat();
    • clearer errors.
  • LinearOperator.tosparse(format='csr') is no longer abstract either. It defaults to LinearOperator.toarray(is_sparse=True).
  • Removed toarray/tosparse overrides that only raised NotImplementedError, so the defaults apply: MatrixFreeLinearOperator, InverseLinearOperator, ComposedLinearOperator.toarray, PowerLinearOperator, DistributedFFTBase, KroneckerLinearSolver.

The default costs one dot per degree of freedom, so it's meant for testing and small problems. Classes that store an explicit matrix keep their own faster versions.

Testing:

  • I checked the defaults against StencilMatrix/BlockLinearOperator (including a nested block), periodic and non-periodic, all 7 sparse formats, plus matrix-free, composed, power and CG-inverse operators. Results matched in serial and with 2 and 4 MPI ranks.
  • feectools linalg tests: same results as devel-tiny (the only failures are the existing petsc ones).
  • Paired struphy PR (runs the struphy test suite against this branch): see the link in the comments.

See this struphy PR: struphy-hub/struphy#662

🤖 Generated with Claude Code

spossann and others added 2 commits October 2, 2026 13:46
Replace the abstract LinearOperator.toarray() by a default implementation
that assembles the global matrix column by column from dot(e_j). Works for
any operator (also matrix-free) on Stencil/(nested) Block vector spaces, in
serial and parallel (full matrix on every rank), dense or scipy.sparse.
Ported and improved from struphy's LinOpWithTransp.toarray_struphy().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tosparse(format='csr') is no longer abstract and defaults to the generic
LinearOperator.toarray(is_sparse=True). Remove the toarray/tosparse overrides
that only raised NotImplementedError (MatrixFreeLinearOperator,
InverseLinearOperator, ComposedLinearOperator.toarray, PowerLinearOperator,
DistributedFFTBase, KroneckerLinearSolver), so the generic methods apply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@spossann

spossann commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

Paired struphy PR (runs the struphy test suite against this branch): struphy-hub/struphy#662

spossann and others added 2 commits October 2, 2026 14:09
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Serial and parallel tests on Stencil, Block and nested Block domains,
all sparse formats, in-place output, composed/power/inverse operators and
invalid input, against the explicit StencilMatrix/BlockLinearOperator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@spossann

spossann commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

Added unit tests in feectools/linalg/tests/test_toarray.py (171383e). Locally: 13 passed in serial, and the 6 parallel tests passed on 2 and 4 ranks. Also bumped the version to 0.2.0 (2b30751).

@spossann
spossann requested a review from max-models October 2, 2026 12:23
@max-models
max-models merged commit 2e3aa65 into devel-tiny Oct 2, 2026
9 checks passed
spossann added a commit to struphy-hub/struphy that referenced this pull request Oct 3, 2026
…OpWithTransp (#662)

**Solves the following issue(s):**

Paired with struphy-hub/feectools#89. The feectools submodule points to
that PR's branch, so struphy's tests run against it.

**Core changes:**

- `LinOpWithTransp` is removed. Its only extra was `toarray_struphy()`,
since `transpose` is already abstract in feectools. That method is now
the default `LinearOperator.toarray()` in feectools (feectools#89), so
all struphy operators subclass `LinearOperator` directly.
- Removed `toarray`/`tosparse` overrides that only raised
`NotImplementedError` (several declared as properties) or just forwarded
to `toarray(is_sparse=True)`. These classes now use the feectools
defaults: the basis projection operators,
`StencilMatrixFreeMassOperator`, `AverageOperator`, the preconditioners,
projectors, variational transport operators, polar operators,
`BoundaryOperator`, and `GT_MAT_G`.
- Tests that used `toarray_struphy()` now call
`LinearOperator.toarray(M, ...)`.
- Feectools submodule bumped to the feectools#89 branch.

**Model-specific changes:**

None

**Documentation changes:**

None

**Before merging:** merge feectools#89 into `devel-tiny` first, then
move the submodule to the new `devel-tiny` head. Until then the
"feectools submodule freshness" check is expected to fail.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants