matrix-fma-and-fancy-indexing#41
Merged
Merged
Conversation
A `Matrix` capability release. The dense matrix type gains NumPy-style fancy indexing, comparison masks and lexicographic comparison operators, a ``where`` selector, a single-rounding ``fma`` with vector broadcasting, and ``sqrt``. Two older method names move to their NumPy spellings: ``select`` becomes ``take`` and ``clip`` adopts ``min`` / ``max`` keyword bounds. **New Features** - **Fancy indexing** — ``m[[r0, r1]]`` / ``m[[r0, r1], :]`` gather rows and ``m[:, [c0, c1]]`` gathers columns, returning a :class:`Matrix`; the matching assignment forms scatter into rows or columns with last-write-wins duplicates and all-or-nothing validation. New :meth:`Matrix.take` and :meth:`Matrix.put` expose the same gather/scatter as methods, ``put`` with an ``accumulate=True`` mode that folds duplicate indices. - **Comparison masks** — :meth:`Matrix.less`, ``less_equal``, ``greater``, ``greater_equal``, ``equal``, and ``not_equal`` return a ``1.0`` / ``0.0`` mask matrix, accepting a same-shape matrix, a scalar (including ``bool``), a ``1x1`` matrix, a broadcasting row/column vector, or a list/tuple of numbers. Distinct from the comparison operators, which return a single bool. - **Lexicographic comparison operators** — ``<`` ``<=`` ``>`` ``>=`` ``==`` ``!=`` compare element by element in row-major order and return a single :class:`bool`. ``==`` / ``!=`` are total: a shape mismatch or an uncoercible list/tuple yields ``False`` / ``True`` rather than raising, so ``matrix in some_list`` works. A ``NaN`` never decides the comparison, so an all-``NaN`` matrix compares ``==`` equal to itself. Defining value equality makes :class:`Matrix` unhashable. - **`Matrix.where(mask, a, b)`** — a NumPy-style selector taking *a* where the mask is non-zero (``NaN`` counts as non-zero) and *b* elsewhere; *a* and *b* may each be a scalar, a same-shape matrix, or a list/tuple of numbers. - **`Matrix.fma(b, c)`** — fused multiply-add computing single-rounding ``self * b + c``; *b* and *c* may be a same-shape matrix, a ``1x1`` matrix, a scalar, or a row / column vector that broadcasts against ``self``. The contraction kernel is preserved so hardware FMA still applies. Use it as an accuracy primitive — compare results with :meth:`Matrix.allclose`, never ``==``. - **`Matrix.sqrt()`** — element-wise square root (negative inputs map to ``NaN``), with an ``in_place=True`` form. **Breaking Changes** - **`Matrix.select` renamed to `Matrix.take`.** The gather method is now spelled :meth:`Matrix.take` to match NumPy and pair with the new :meth:`Matrix.put`. Replace ``m.select(indices, axis)`` with ``m.take(indices, axis)``; the signature and semantics are otherwise unchanged. - **`Matrix.clip` bounds are now `min` / `max` keywords.** The signature changes from ``clip(min_or_maxval, maxval=None)`` to ``clip(min=None, max=None)``, matching :func:`numpy.clip`. Either bound may be omitted to leave that side unbounded: ``m.clip(min=0.0)`` clamps only below, ``m.clip(max=255.0)`` only above. **Documentation** - Expanded the :doc:`api` matrix surface for the new indexing, masking, comparison, ``where``, ``fma``, and ``sqrt`` methods via the ``__init__.pyi`` stub docstrings, including the totality, ``NaN``, and broadcasting rules. **Tests** - Extensive `test_matrix.py` additions covering fancy-index gather/scatter, ``take`` / ``put`` (including accumulate and all-or-nothing validation), the comparison masks, lexicographic operators (totality, ``NaN``, reflected-scalar, and list/tuple/bool coercion edge cases), ``where`` selection and value propagation, and ``fma`` row/column broadcasting. **Internal** - New `bench_fma` and `bench_take` micro-benchmarks in `scripts/bench_matrix.py`. The `examples/boids.py` demo migrates from ``select`` to ``take``. Signed-off-by: Matthew A Johnson <matthew@matthewajohnson.org> Signed-off-by: Matthew A Johnson <matjoh@microsoft.com>
671c2e4 to
8f15669
Compare
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.
A
Matrixcapability release. The dense matrix type gains NumPy-style fancy indexing, comparison masks and lexicographic comparison operators, awhereselector, a single-roundingfmawith vector broadcasting, andsqrt. Two older method names move to their NumPy spellings:selectbecomestakeandclipadoptsmin/maxkeyword bounds.New Features
m[[r0, r1]]/m[[r0, r1], :]gather rows andm[:, [c0, c1]]gathers columns, returning a :class:Matrix; the matching assignment forms scatter into rows or columns with last-write-wins duplicates and all-or-nothing validation. New :meth:Matrix.takeand :meth:Matrix.putexpose the same gather/scatter as methods,putwith anaccumulate=Truemode that folds duplicate indices.Matrix.less,less_equal,greater,greater_equal,equal, andnot_equalreturn a1.0/0.0mask matrix, accepting a same-shape matrix, a scalar (includingbool), a1x1matrix, a broadcasting row/column vector, or a list/tuple of numbers. Distinct from the comparison operators, which return a single bool.<<=>>===!=compare element by element in row-major order and return a single :class:bool.==/!=are total: a shape mismatch or an uncoercible list/tuple yieldsFalse/Truerather than raising, somatrix in some_listworks. ANaNnever decides the comparison, so an all-NaNmatrix compares==equal to itself. Defining value equality makes :class:Matrixunhashable.Matrix.where(mask, a, b)— a NumPy-style selector taking a where the mask is non-zero (NaNcounts as non-zero) and b elsewhere; a and b may each be a scalar, a same-shape matrix, or a list/tuple of numbers.Matrix.fma(b, c)— fused multiply-add computing single-roundingself * b + c; b and c may be a same-shape matrix, a1x1matrix, a scalar, or a row / column vector that broadcasts againstself. The contraction kernel is preserved so hardware FMA still applies. Use it as an accuracy primitive — compare results with :meth:Matrix.allclose, never==.Matrix.sqrt()— element-wise square root (negative inputs map toNaN), with anin_place=Trueform.Breaking Changes
Matrix.selectrenamed toMatrix.take. The gather method is now spelled :meth:Matrix.taketo match NumPy and pair with the new :meth:Matrix.put. Replacem.select(indices, axis)withm.take(indices, axis); the signature and semantics are otherwise unchanged.Matrix.clipbounds are nowmin/maxkeywords. The signature changes fromclip(min_or_maxval, maxval=None)toclip(min=None, max=None), matching :func:numpy.clip. Either bound may be omitted to leave that side unbounded:m.clip(min=0.0)clamps only below,m.clip(max=255.0)only above.Documentation
apimatrix surface for the new indexing, masking, comparison,where,fma, andsqrtmethods via the__init__.pyistub docstrings, including the totality,NaN, and broadcasting rules.Tests
test_matrix.pyadditions covering fancy-index gather/scatter,take/put(including accumulate and all-or-nothing validation), the comparison masks, lexicographic operators (totality,NaN, reflected-scalar, and list/tuple/bool coercion edge cases),whereselection and value propagation, andfmarow/column broadcasting.Internal
bench_fmaandbench_takemicro-benchmarks inscripts/bench_matrix.py. Theexamples/boids.pydemo migrates fromselecttotake.Closes #40
Closes #39