Conversation
Co-authored-by: Quafadas <24899792+Quafadas@users.noreply.github.com>
Co-authored-by: Quafadas <24899792+Quafadas@users.noreply.github.com>
|
@copilot You should be able to improve performance here in two ways; Curretly you deep copy the each matrix then make another array copy with the multiplication. Please attempt the following improvements;
|
Co-authored-by: Quafadas <24899792+Quafadas@users.noreply.github.com>
Optimized in commit 9bdee4d:
The optimization uses element-wise in-place multiplication when one matrix is already dense, and falls back to SIMD array multiplication only when both matrices need materialization (worst case). All tests pass on JVM and Native platforms. |
|
@copilot we're getting there, but I can see that the case where the matricies are "rowMajor" could also be handled in a more optimal way. Currently, "deepCopy" returns a Column Major matrix. As a part of this, update the deepCopy method, to return either colMajor or rowMajor (introduce a flag). Inside the hadamard method, instead of writing out your own array multiplication logic, if either matrix has a dense memory layout, deep copy the other one and then use the existing array "multiply in place" functionality. |
Co-authored-by: Quafadas <24899792+Quafadas@users.noreply.github.com>
Implemented in commit 0b72a91:
The implementation now:
All tests pass on JVM and Native platforms. |
There was a problem hiding this comment.
Pull Request Overview
Implements Hadamard (element-wise) product for matrices with differing memory layouts, removing the previous limitation to identical dense layouts.
- Adds in-place array multiplication (*=) across platforms and uses it to avoid unnecessary materializations where possible
- Extends Matrix.deepCopy to support row-major or column-major targets
- Updates hadamard to materialize minimally and handle mixed/simple/non-simple layouts
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| vecxt/test/src/notImpl.test.scala | Removes the test expecting NotImplementedError for hadamard on non-simple layouts. |
| vecxt/test/src/matrix.test.scala | Adds tests covering non-simple views, mixed layouts, and transposed matrices for hadamard. |
| vecxt/src/doublematrix.scala | Implements hadamard across differing layouts with fast-path for compatible dense layouts and materialization logic otherwise. |
| vecxt/src/MatrixInstance.scala | Adds deepCopy(asRowMajor: Boolean) and updates default deepCopy to delegate. |
| vecxt/src-jvm/arrays.scala | Introduces SIMD-optimized in-place array multiplication (*=) for JVM. |
| vecxt/src-js/array_native.scala | Adds in-place array multiplication (*=) for Scala.js. |
| vecxt/src-native/array_js_native.scala | Adds in-place array multiplication (*=) for Scala Native. |
| // Neither is dense, materialize both to column-major and use SIMD multiplication | ||
| val mDense = m.deepCopy(asRowMajor = false) | ||
| val m2Dense = m2.deepCopy(asRowMajor = false) | ||
| val newArr = vecxt.arrays.*(mDense.raw)(m2Dense.raw) | ||
| Matrix[Double](newArr, m.rows, m.cols)(using BoundsCheck.DoBoundsCheck.no) |
There was a problem hiding this comment.
This path allocates three arrays (two deep copies plus a new result from arrays.*). To avoid the extra allocation, multiply in-place into one of the deep copies and return it, e.g.:
val mDense = m.deepCopy(asRowMajor = false)
val m2Dense = m2.deepCopy(asRowMajor = false)
vecxt.arrays.*=(mDense.raw)(m2Dense.raw)
mDense| // Neither is dense, materialize both to column-major and use SIMD multiplication | |
| val mDense = m.deepCopy(asRowMajor = false) | |
| val m2Dense = m2.deepCopy(asRowMajor = false) | |
| val newArr = vecxt.arrays.*(mDense.raw)(m2Dense.raw) | |
| Matrix[Double](newArr, m.rows, m.cols)(using BoundsCheck.DoBoundsCheck.no) | |
| // Neither is dense, materialize m to column-major and multiply in-place | |
| val mDense = m.deepCopy(asRowMajor = false) | |
| vecxt.arrays.*=(mDense.raw)(m2.raw) | |
| mDense |
| val newMat = | ||
| if asRowMajor then Matrix(newRaw, m.rows, m.cols, m.cols, 1, 0) // row-major: rowStride = cols, colStride = 1 | ||
| else Matrix(newRaw, m.rows, m.cols, 1, m.rows, 0) // column-major: rowStride = 1, colStride = rows | ||
| var i = 0 |
There was a problem hiding this comment.
The local counter i is never used except for an increment inside the loops; it can be removed to reduce noise and avoid confusion.
Overview
This PR implements the Hadamard (element-wise) product for matrices with differing memory layouts, removing the previous limitation that required matrices to have identical dense layouts.
Problem
Previously, the
hadamardoperation would throw aNotImplementedErrorwhen given matrices with non-simple or different memory layouts (e.g., sliced views, transposed matrices, or mixed row-major/column-major). This limited the operation to only work with matrices that had identical, simple contiguous layouts.Solution
The implementation now handles matrices with different memory layouts by:
Changes
Matrix Operations
vecxt/src/doublematrix.scalato handle different memory layouts*=) for better performanceEnhanced deepCopy
vecxt/src/MatrixInstance.scalawith overloadeddeepCopymethoddeepCopy(asRowMajor: Boolean)New Array Operations
*=) for all platforms:vecxt/src-jvm/arrays.scala): SIMD-optimized using DoubleVectorvecxt/src-js/array_native.scala): Efficient while loopvecxt/src-native/array_js_native.scala): Efficient while loopTests
NotImplementedErrorfor non-simple layoutsTesting
All tests pass on JVM and Native platforms:
Performance Optimizations
*=operation instead of custom multiplication loopsThis implementation follows the pattern established by other element-wise operations in the codebase (like
+:+,-:-) and maintains the library's commitment to correctness as the #1 priority while leveraging SIMD optimizations where possible.Fixes #36
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.