Skip to content
Open

Docs #157

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions benchmark_vs_breeze/src/doSomeStuff.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ import jdk.incubator.vector.DoubleVector
import breeze.linalg.*

//% mill benchmark_vs_breeze.runJmh -jvmArgs --add-modules=jdk.incubator.vector

/** Benchmark (matDim) Mode Cnt Score Error Units LinearAlgebraWorkloadBenchmark.breezeWorkload 1000 thrpt 10 5.072 ±
* 0.292 ops/s LinearAlgebraWorkloadBenchmark.vecxtWorkload 1000 thrpt 10 7.913 ± 0.514 ops/s
*/
@State(Scope.Thread)
class LinearAlgebraWorkloadBenchmark extends BLASBenchmark:

@Param(Array("500"))
@Param(Array("1000"))
var matDim: String = uninitialized
var matDimInt: Int = uninitialized

var dataA: Array[Double] = uninitialized
var dataB: Array[Double] = uninitialized
Expand All @@ -33,6 +38,7 @@ class LinearAlgebraWorkloadBenchmark extends BLASBenchmark:
def setup(): Unit =
println(s"[SETUP] Running setup for matrix dim: $matDim")
val dim = matDim.toInt
matDimInt = dim
dataA = randomDoubleArray(dim * dim)
dataB = randomDoubleArray(dim * dim)
vectorData = randomDoubleArray(dim)
Expand Down Expand Up @@ -69,12 +75,15 @@ class LinearAlgebraWorkloadBenchmark extends BLASBenchmark:
val step3 = step2 * breezeVec // Matrix-vector multiply
val step4 = step3.map(_ * 2.0 + 1.0) // Element-wise transform
val step5 = breeze.linalg.norm(step4) // L2 norm
// val step6 = step2.t // Transpose
val step6: DenseMatrix[Double] = step2.t // Transpose
val step7 = breeze.linalg.sum(step2) // Sum reduction
val step8 = (step7 > 0.5) // Comparison
val matmul = step6 * step1

// Combine results to prevent dead code elimination
val result = step5 + (if step8 then 1.0 else 0.0) + breeze.linalg.max(step4)
val result = step5 + (if step8 then 1.0 else 0.0) + breeze.linalg.max(step4) + breeze.linalg.sum(
step6(10 until matDimInt, 10 until matDimInt)
)
bh.consume(result)
end breezeWorkload

Expand All @@ -83,16 +92,18 @@ class LinearAlgebraWorkloadBenchmark extends BLASBenchmark:

// Same representative linear algebra workload
val step1 = vecxtMatA + vecxtMatB // Element-wise addition
val step2 = step1.hadamard(vecxtMatA) // Hadamard product
val step2 = step1 * vecxtMatA // Hadamard product
val step3 = step2 * vectorData // Matrix-vector multiply
val step4 = step3.fma(2.0, 1.0) // Element-wise transform
val step5 = step4.norm // L2 norm
// val step6 = step2.transpose // Transpose
val step6 = step2.transpose // Transpose
val step7 = step2.sum // Sum reduction
val step8 = (step7 > 0.5) // Comparison
val matmul = step6 @@ step1

// Combine results to prevent dead code elimination
val result = step5 + (if step8 then 1.0 else 0.0) + step4.maxSIMD
val result =
step5 + (if step8 then 1.0 else 0.0) + step4.maxSIMD + step6(10 until matDimInt, 10 until matDimInt).sum
bh.consume(result)
end vecxtWorkload

Expand Down
44 changes: 41 additions & 3 deletions site/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ Getting started with scala cli
```scala sc:nocompile
ivy"io.github.quafadas::vecxt::@VERSION@"
```
### Intro

The obvious [vector operations](vectors-and-matrices/examples.md).

```scala mdoc
import vecxt.all.*
Expand All @@ -31,20 +34,55 @@ v1 + v2
val v3 = Array.fill(3)(0.0)
v3 -= v2
v3
```
The core of `vecxt` is nothing more than a bunch of extension methods on `Array[Double]`, `Array[Float]` etc... design stupid... but attractively simple.

Matricies look like this.

```scala mdoc
import vecxt.all.*

val m1 = Matrix.fromRows[Double](
Array(1.0, 2.0, 3.0),
Array(4.0, 5.0, 6.0),
Array(7.0, 8.0, 9.0)
)

println(m1.printMat)

val t1 = m1.transpose // zero copy
println(t1.printMat)
val slice1 = m1(1 to 2, 1 to 2) // zero copy

slice1.shape

val matmul = (1.0 + m1) @@ t1( :: , 0 to 1)
// 1.0 + m1 -> SIMD accelerated
// t(::, 0 to 1) -> slice of a slice, still zero copy
// m @@ m -> BLAS accelerated

println(matmul.shape)
println(matmul.printMat)

```

NDArray

```scala

```

## Goals

- Pythonic syntax
- Where possible inline calls to platform-native-BLAS implementations for maximum performance\
- Zero copy semantics / views on contiguous unbroken arrays of data for performance
- Reasonable, consistent cross platform ergonomics
- Very little / no data-structures - the vector part of the library is an extension method on `Array[Double]` for example
- Very few custom data-structures - the vector part of the library is an extension method on `Array[Double]` for example
- A single cross platform test suite
- Simplicity, speed

## Non-Goals

- General mathematics - lets' try to keep a focus on linear algebra. See [slash](https://github.com/dragonfly-ai/slash) or [breeze](https://github.com/scalanlp/breeze/) for more general libraries
- Visualisation - see [dedav4s](https://quafadas.github.io/dedav4s/)
- Visualisation - see [dedav4s](https://quafadas.github.io/dedav4s/)
- Data ingestion - see [scautable](https://github.com/Quafadas/scautable)
16 changes: 12 additions & 4 deletions site/docs/vectors-and-matrices/examples.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# Array Examples

Some basic examples with doubles.
Vectors are modelled as `Array[?]`. This has attractive performance characteristics.

Note: Floats should have the same API as the `Double` examples below. The API demo below is not complete - particulaly in respect of the mutating API.


```scala mdoc
import vecxt.all.{*, given}
import vecxt.all.*
val v1 = Array[Double](1, 2, 3)
val v2 = Array[Double](4, 5, 6)
val chaos = Array[Double](4, 5, 6)

v1.dot(v2)

Expand All @@ -20,9 +24,7 @@ v1.productSIMD
v1.norm

v1.clampMin(1.5).printArr

v2.clampMax(1.5).printArr

v1.clamp(1.5, 2.5 ).printArr

v1.maxSIMD
Expand All @@ -44,10 +46,16 @@ v1.logSumExp
v1.cumsum.printArr

(v1 + 1.0).printArr
chaos += 1.0
chaos.printArr

(v1 + v2).printArr
chaos += v2
chaos.printArr

(v1 - 1.0).printArr
chaos -= 1.0
chaos.printArr

(v1 - v2).printArr

Expand Down
16 changes: 2 additions & 14 deletions vecxt/src-jvm/doublearrays.scala
Original file line number Diff line number Diff line change
Expand Up @@ -853,13 +853,7 @@ object doublearrays:
out
end clampMax

inline def maxClamp(ceil: Double): Array[Double] =
val out = vec.clone
out.`clampOp!`(VectorOperators.GT, ceil)
out
end maxClamp

inline def `maxClamp!`(ceil: Double): Unit =
inline def `clampMax!`(ceil: Double): Unit =
vec.`clampOp!`(VectorOperators.GT, ceil)

/** Clamps the values in the array to a minimum value.
Expand All @@ -875,13 +869,7 @@ object doublearrays:
out
end clampMin

inline def minClamp(floor: Double): Array[Double] =
val out = vec.clone
out.`clampOp!`(VectorOperators.LT, floor)
out
end minClamp

inline def `minClamp!`(floor: Double): Unit =
inline def `clampMin!`(floor: Double): Unit =
vec.`clampOp!`(VectorOperators.LT, floor)

/** Clamps the values in the array to a specified range.
Expand Down
Loading
Loading