From df5e0a771f3c94114c23dc418d715a10bba1c05a Mon Sep 17 00:00:00 2001 From: Simon Parten Date: Wed, 11 Dec 2024 21:30:32 +0100 Subject: [PATCH 1/5] A start on integers --- site/docs/_docs/examples.mdoc.md | 15 ++++++++++++- vecxt/jvm/src/arrays.scala | 36 +++++++++++++++++++++++++----- vecxt/test/src/intarray.test.scala | 6 +++++ 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/site/docs/_docs/examples.mdoc.md b/site/docs/_docs/examples.mdoc.md index 5168147..02e8608 100644 --- a/site/docs/_docs/examples.mdoc.md +++ b/site/docs/_docs/examples.mdoc.md @@ -3,7 +3,7 @@ title: Vector Examples --- # Vector Examples -Some basic exampeles. +Some basic examples with doubles. ```scala mdoc import vecxt.all.* @@ -33,3 +33,16 @@ cosineSimilarity(v1, v2) (v1(v1 <= 2)).printArr ``` +And Ints + +```scala mdoc +import vecxt.all.* +import narr.* +import vecxt.BoundsCheck.DoBoundsCheck.yes + +val v1 = NArray(1, 2, 3) +val v2 = NArray(4, 5, 6) +(v1 + v2).printArr + + +``` \ No newline at end of file diff --git a/vecxt/jvm/src/arrays.scala b/vecxt/jvm/src/arrays.scala index a28ff90..d8a2ec6 100644 --- a/vecxt/jvm/src/arrays.scala +++ b/vecxt/jvm/src/arrays.scala @@ -241,23 +241,49 @@ object arrays: inline def -(vec2: Array[Int])(using inline boundsCheck: BoundsCheck): Array[Int] = dimCheck(vec, vec2) - val newVec = Array.ofDim[Int](vec.length) + vec.clone.tap(_ -= vec2) + end - + + inline def -=(vec2: Array[Int])(using inline boundsCheck: BoundsCheck): Unit = + dimCheck(vec, vec2) var i = 0 while i < spi.loopBound(vec.length) do IntVector .fromArray(spi, vec, i) .sub(IntVector.fromArray(spi, vec2, i)) - .intoArray(newVec, i) + .intoArray(vec, i) i += spil end while while i < vec.length do - newVec(i) = vec(i) - vec2(i) + vec(i) = vec(i) - vec2(i) i += 1 end while - newVec - end - + end -= + + inline def +(vec2: Array[Int])(using inline boundsCheck: BoundsCheck): Array[Int] = + dimCheck(vec, vec2) + vec.clone.tap(_ += vec2) + end + + + inline def +=(vec2: Array[Int])(using inline boundsCheck: BoundsCheck): Unit = + dimCheck(vec, vec2) + var i = 0 + + while i < spi.loopBound(vec.length) do + IntVector + .fromArray(spi, vec, i) + .add(IntVector.fromArray(spi, vec2, i)) + .intoArray(vec, i) + i += spil + end while + + while i < vec.length do + vec(i) = vec(i) + vec2(i) + i += 1 + end while + end += end extension diff --git a/vecxt/test/src/intarray.test.scala b/vecxt/test/src/intarray.test.scala index 3678c40..4e2a409 100644 --- a/vecxt/test/src/intarray.test.scala +++ b/vecxt/test/src/intarray.test.scala @@ -31,6 +31,12 @@ class IntArrayExtensionSuite extends munit.FunSuite: assertVecEquals((v2 - v1), NArray.fill[Int](v1.length)(-1)) } + test("+") { + val v2 = NArray.tabulate[Int](1, 2, 3, 4, 5, 6, 7, 8, 9) + val v1 = NArray(1, 2, 3, 4, 5, 6, 7, 8, 9) + assertVecEquals((v2 + v1), v1.dot(v2)) + } + test("sum") { val v1 = NArray.tabulate[Int](10)(i => i) From 208c0377f9524ea78b7936eb13e479583f600d3c Mon Sep 17 00:00:00 2001 From: Simon Parten Date: Wed, 11 Dec 2024 22:02:10 +0100 Subject: [PATCH 2/5] js and native --- site/docs/_docs/examples.mdoc.md | 3 +++ vecxt/js-native/src/array.scala | 26 ++++++++++++++++++++++++++ vecxt/jvm/src/arrays.scala | 24 ++++++++++++++++++++++++ vecxt/test/src/intarray.test.scala | 10 ++++++++-- 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/site/docs/_docs/examples.mdoc.md b/site/docs/_docs/examples.mdoc.md index 02e8608..e87c07d 100644 --- a/site/docs/_docs/examples.mdoc.md +++ b/site/docs/_docs/examples.mdoc.md @@ -44,5 +44,8 @@ val v1 = NArray(1, 2, 3) val v2 = NArray(4, 5, 6) (v1 + v2).printArr +v1.dot(v2) + +(v1 - v2).printArr ``` \ No newline at end of file diff --git a/vecxt/js-native/src/array.scala b/vecxt/js-native/src/array.scala index 505e358..ba38889 100644 --- a/vecxt/js-native/src/array.scala +++ b/vecxt/js-native/src/array.scala @@ -129,6 +129,32 @@ object JsNativeDoubleArrays: res end - + inline def +(other: NArray[Int])(using inline boundsCheck: BoundsCheck): NArray[Int] = + dimCheck(vec, other) + val n = vec.length + val res = NArray.fill(n)(0) + + var i = 0 + while i < n do + res(i) = vec(i) + other(i) + i = i + 1 + end while + res + end + + + inline def dot(other: NArray[Int])(using inline boundsCheck: BoundsCheck): Int = + dimCheck(vec, other) + val n = vec.length + var sum = 0 + + var i = 0 + while i < n do + sum += vec(i) * other(i) + i = i + 1 + end while + sum + end dot + inline def <(num: Int): NArray[Boolean] = logicalIdx((a, b) => a < b, num) diff --git a/vecxt/jvm/src/arrays.scala b/vecxt/jvm/src/arrays.scala index d8a2ec6..d073478 100644 --- a/vecxt/jvm/src/arrays.scala +++ b/vecxt/jvm/src/arrays.scala @@ -239,6 +239,30 @@ object arrays: temp end sum + inline def dot(vec2: Array[Int])(using inline boundsCheck: BoundsCheck): Int = + dimCheck(vec, vec2) + val newVec = Array.ofDim[Int](vec.length) + var i = 0 + var acc = IntVector.zero(spi) + + while i < spi.loopBound(vec.length) do + acc = IntVector + .fromArray(spi, vec, i) + .mul(IntVector.fromArray(spi, vec2, i)) + .add(acc) + + i += spil + end while + + var temp = acc.reduceLanes(VectorOperators.ADD) + + while i < vec.length do + temp += vec(i) * vec2(i) + i += 1 + end while + temp + end dot + inline def -(vec2: Array[Int])(using inline boundsCheck: BoundsCheck): Array[Int] = dimCheck(vec, vec2) vec.clone.tap(_ -= vec2) diff --git a/vecxt/test/src/intarray.test.scala b/vecxt/test/src/intarray.test.scala index 4e2a409..aed6928 100644 --- a/vecxt/test/src/intarray.test.scala +++ b/vecxt/test/src/intarray.test.scala @@ -32,9 +32,15 @@ class IntArrayExtensionSuite extends munit.FunSuite: } test("+") { - val v2 = NArray.tabulate[Int](1, 2, 3, 4, 5, 6, 7, 8, 9) + val v2 = NArray(1, 2, 3, 4, 5, 6, 7, 8, 9) val v1 = NArray(1, 2, 3, 4, 5, 6, 7, 8, 9) - assertVecEquals((v2 + v1), v1.dot(v2)) + assertVecEquals((v2 + v1), NArray(2, 4, 6, 8, 10, 12, 14, 16, 18)) + } + + test("dot") { + val v1 = NArray.tabulate[Int](10)(i => i) + val v2 = NArray.tabulate[Int](10)(i => i) + assertEquals(v1.dot(v2), 285) } test("sum") { From 4d473741cf252af8b4893d3fc331ca035c4d2ea2 Mon Sep 17 00:00:00 2001 From: Simon Parten Date: Wed, 11 Dec 2024 22:45:49 +0100 Subject: [PATCH 3/5] arg --- site/docs/_docs/examples.mdoc.md | 12 ++++++++- vecxt/js-native/src/array.scala | 2 ++ vecxt/js/src/array.scala | 36 +++++++++++++++++++++----- vecxt/jvm/src/arrays.scala | 10 ++++--- vecxt/native/src/array.scala | 32 ++++++++++++++++++----- vecxt/test/src/array.test.scala | 5 ++++ vecxt/test/src/booleanarray.test.scala | 1 - 7 files changed, 78 insertions(+), 20 deletions(-) diff --git a/site/docs/_docs/examples.mdoc.md b/site/docs/_docs/examples.mdoc.md index e87c07d..06fa443 100644 --- a/site/docs/_docs/examples.mdoc.md +++ b/site/docs/_docs/examples.mdoc.md @@ -42,10 +42,20 @@ import vecxt.BoundsCheck.DoBoundsCheck.yes val v1 = NArray(1, 2, 3) val v2 = NArray(4, 5, 6) -(v1 + v2).printArr + v1.dot(v2) +(v1 + v2).printArr + (v1 - v2).printArr +(v1 > 2).printArr +(v1 >= 2).printArr + +(v1 < 2).printArr +(v1 <= 2).printArr + +(v1(v1 <= 2)).printArr + ``` \ No newline at end of file diff --git a/vecxt/js-native/src/array.scala b/vecxt/js-native/src/array.scala index ba38889..3dff968 100644 --- a/vecxt/js-native/src/array.scala +++ b/vecxt/js-native/src/array.scala @@ -6,6 +6,7 @@ import vecxt.BoundsCheck.BoundsCheck import scala.math.Ordering import narr.* +import scala.reflect.ClassTag object JsNativeBooleanArrays: @@ -131,6 +132,7 @@ object JsNativeDoubleArrays: inline def +(other: NArray[Int])(using inline boundsCheck: BoundsCheck): NArray[Int] = dimCheck(vec, other) + val n = vec.length val res = NArray.fill(n)(0) diff --git a/vecxt/js/src/array.scala b/vecxt/js/src/array.scala index 2c75ea0..085ac1b 100644 --- a/vecxt/js/src/array.scala +++ b/vecxt/js/src/array.scala @@ -22,6 +22,9 @@ import scala.util.chaining.* import vecxt.BoundsCheck.BoundsCheck import narr.* +import scala.reflect.ClassTag +import scala.scalajs.js.annotation.JSBracketAccess +import vecxt.JsNativeBooleanArrays.trues object arrayUtil: extension [A](d: Array[A]) def printArr: String = d.mkString("[", ",", "]") @@ -52,12 +55,12 @@ object arrays: extension [A](v: js.Array[A]) inline def fill(a: A): Unit = v.asInstanceOf[JsArrayFacade].fill(a) extension (vec: js.Array[Boolean]) - inline def countTrue: Int = - var sum = 0 - for i <- 0 until vec.length do if vec(i) then sum = sum + 1 - end for - sum - end countTrue + // inline def trues: Int = + // var sum = 0 + // for i <- 0 until vec.length do if vec(i) then sum = sum + 1 + // end for + // sum + // end trues inline def &&(thatIdx: js.Array[Boolean]): js.Array[Boolean] = val result = new js.Array[Boolean](vec.length) @@ -88,12 +91,31 @@ object arrays: inline def update(idx: Int, d: Double)(using inline boundsCheck: BoundsCheck.BoundsCheck): Unit = indexCheck(vec, idx) vec(idx) = d + end update + + end extension + + extension (vec: NArray[Int]) + + def apply(index: js.Array[Boolean]): NArray[Int] = + val truely = index.trues + val newVec = NArray.ofSize[Int](truely) + var j = 0 + for i <- 0 until index.length do + // println(s"i: $i || j: $j || ${index(i)} ${vec(i)} ") + if index(i) then + newVec(j) = vec(i) + j += 1 + end for + newVec + end apply + end extension extension (vec: NArray[Double]) inline def apply(index: js.Array[Boolean])(using inline boundsCheck: BoundsCheck.BoundsCheck) = dimCheck(vec, index) - val trues = index.countTrue + val trues = index.trues val newVec = Float64Array(trues) var j = 0 for i <- 0 until index.length do diff --git a/vecxt/jvm/src/arrays.scala b/vecxt/jvm/src/arrays.scala index d073478..ab032d4 100644 --- a/vecxt/jvm/src/arrays.scala +++ b/vecxt/jvm/src/arrays.scala @@ -24,6 +24,7 @@ import jdk.incubator.vector.ByteVector import jdk.incubator.vector.DoubleVector import jdk.incubator.vector.VectorOperators import jdk.incubator.vector.IntVector +import scala.reflect.ClassTag object arrays: @@ -79,7 +80,6 @@ object arrays: out end any - // TODO this may be sub-optimal - we want to move the accumulator out of the hot loop. inline def trues: Int = var i = 0 var sum = 0 @@ -311,12 +311,11 @@ object arrays: end extension - extension (vec: Array[Double]) - + extension [@specialized(Double, Int) A](vec: Array[A])(using ClassTag[A]) inline def apply(index: Array[Boolean])(using inline boundsCheck: BoundsCheck) = dimCheck(vec, index) val trues = index.trues - val newVec: Array[Double] = new Array[Double](trues) + val newVec: Array[A] = new Array[A](trues) var j = 0 for i <- 0 until index.length do // println(s"i: $i || j: $j || ${index(i)} ${vec(i)} ") @@ -326,6 +325,9 @@ object arrays: end for newVec end apply + end extension + + extension (vec: Array[Double]) /** Apparently, left packing is hard problem in SIMD land. * https://stackoverflow.com/questions/79025873/selecting-values-from-java-simd-doublevector diff --git a/vecxt/native/src/array.scala b/vecxt/native/src/array.scala index 3ea49c3..6a63d9b 100644 --- a/vecxt/native/src/array.scala +++ b/vecxt/native/src/array.scala @@ -22,16 +22,18 @@ import org.ekrich.blas.* import org.ekrich.blas.unsafe.* import vecxt.BoundsCheck.BoundsCheck +import scala.reflect.ClassTag +import vecxt.JsNativeBooleanArrays.trues object arrays: extension (vec: Array[Boolean]) - inline def countTrue: Int = - var sum = 0 - for i <- 0 until vec.length do if vec(i) then sum = sum + 1 - end for - sum - end countTrue + // inline def trues: Int = + // var sum = 0 + // for i <- 0 until vec.length do if vec(i) then sum = sum + 1 + // end for + // sum + // end trues inline def &&(thatIdx: Array[Boolean]): Array[Boolean] = val result: Array[Boolean] = new Array[Boolean](vec.length) @@ -58,10 +60,26 @@ object arrays: // end copy end extension + extension [A: ClassTag](vec: Array[A]) + + def apply(index: Array[Boolean]): Array[A] = + val truely = index.trues + val newVec = Array.ofDim[A](truely) + var j = 0 + for i <- 0 until index.length do + // println(s"i: $i || j: $j || ${index(i)} ${vec(i)} ") + if index(i) then + newVec(j) = vec(i) + j += 1 + end for + newVec + end apply + end extension + extension (vec: Array[Double]) def apply(index: Array[Boolean]) = - val trues = index.countTrue + val trues = index.trues val newVec = new Array[Double](trues) var j = 0 for i <- 0 until index.length do diff --git a/vecxt/test/src/array.test.scala b/vecxt/test/src/array.test.scala index 1b21e2b..5bd0fde 100644 --- a/vecxt/test/src/array.test.scala +++ b/vecxt/test/src/array.test.scala @@ -58,6 +58,11 @@ class ArrayExtensionSuite extends munit.FunSuite: val afterIndex2 = v2(vIdx2) assertEqualsDouble(afterIndex2(4), 8.0, 0.0001) + val v3 = NArray[Int](1, 2, 3, 4, 5, 6, 7, 8, 9) + val vIdx3 = NArray[Boolean](true, false, true, true, false, true, false, true, false) + val afterIndex3 = v3(vIdx3) + assertEquals(afterIndex3(4), 8) + } test("check vector operator precendance") { diff --git a/vecxt/test/src/booleanarray.test.scala b/vecxt/test/src/booleanarray.test.scala index ec44ce0..dbb5067 100644 --- a/vecxt/test/src/booleanarray.test.scala +++ b/vecxt/test/src/booleanarray.test.scala @@ -17,7 +17,6 @@ package vecxt import narr.* -import scala.util.chaining.* import vecxt.all.* class BooleanArrayExtensionSuite extends munit.FunSuite: From 188a05ec883c355ec5cb005f4d91834eca5cac51 Mon Sep 17 00:00:00 2001 From: Simon Parten Date: Thu, 12 Dec 2024 20:44:12 +0100 Subject: [PATCH 4/5] . --- build.mill | 10 +++++++++- jsSite/src/BenchmarkPlots.scala | 2 ++ jsSite/src/PlotElements.scala | 4 ++-- jsSite/src/facade.scala | 2 ++ site/docs/_docs/benchmarks/sum.mdoc.md | 1 + site/docs/_docs/examples.mdoc.md | 4 ++-- vecxt/js/src/array.scala | 10 ---------- vecxt/jvm/src/arrays.scala | 1 + 8 files changed, 19 insertions(+), 15 deletions(-) diff --git a/build.mill b/build.mill index 5b323ad..156369e 100644 --- a/build.mill +++ b/build.mill @@ -1,7 +1,7 @@ import mill.scalalib.publish.Scope.Test import $ivy.`com.github.lolgab::mill-crossplatform::0.2.4` -import $ivy.`io.github.quafadas:millSite_mill0.12_2.13:0.0.36` +import $ivy.`io.github.quafadas:millSite_mill0.12_2.13:0.0.37-DIRTYafea46f4` import $ivy.`de.tototec::de.tobiasroeser.mill.vcs.version::0.4.0` import $ivy.`com.lihaoyi::mill-contrib-jmh:` import $ivy.`com.goyeau::mill-scalafix::0.4.2` @@ -178,6 +178,12 @@ object jsSite extends SiteJSModule { override def moduleDeps = Seq(vecxt.js, vecxtensions.js) override def scalaVersion = vecxt.js.scalaVersion override def scalaJSVersion = vecxt.js.scalaJSVersion + + override def scalaJsCompilerVersion: String = "3.6.2" + + + // override def allScalacOptions: T[Seq[String]] = super.allScalacOptions`() ++ Seq("-experimental", "-language:experimental.namedTuples") + override def scalacOptions: T[Seq[String]] = super.scalacOptions() ++ Seq("-experimental", "-language:experimental.namedTuples") override def moduleKind = ModuleKind.ESModule override def ivyDeps = super.ivyDeps() ++ Agg( ivy"org.scala-js::scalajs-dom::2.8.0", @@ -206,4 +212,6 @@ object site extends SiteModule { override def moduleDeps = Seq(vecxt.jvm) override def scalaDocOptions = super.scalaDocOptions + override def scalacOptions: T[Seq[String]] = super.scalacOptions() ++ Seq("-experimental", "-language:experimental.namedTuples") + } \ No newline at end of file diff --git a/jsSite/src/BenchmarkPlots.scala b/jsSite/src/BenchmarkPlots.scala index 2d52745..40b988e 100644 --- a/jsSite/src/BenchmarkPlots.scala +++ b/jsSite/src/BenchmarkPlots.scala @@ -4,7 +4,9 @@ import scala.NamedTuple.AnyNamedTuple import scala.annotation.meta.field import upickle.default.* import NamedTupleReadWriter.given +import scala.annotation.experimental +@experimental() object BenchmarkPlots: def addScalarBenchmark: String = val thePlot = BenchmarkPlotElements.schema ++ diff --git a/jsSite/src/PlotElements.scala b/jsSite/src/PlotElements.scala index 00e0894..0c359d2 100644 --- a/jsSite/src/PlotElements.scala +++ b/jsSite/src/PlotElements.scala @@ -1,13 +1,13 @@ package vecxt.plot -import scala.NamedTuple.* +// import scala.NamedTuple.* import scala.annotation.meta.field import upickle.default.* import NamedTupleReadWriter.given import scala.collection.immutable.Stream.Empty object BenchmarkPlotElements: - final val schema = (`$schema` = "https://vega.github.io/schema/vega-lite/v5.json") + final val schema: ($schema: String) = (`$schema` = "https://vega.github.io/schema/vega-lite/v5.json") val fakeData = ( data = ( diff --git a/jsSite/src/facade.scala b/jsSite/src/facade.scala index 1e2823d..2e493fd 100644 --- a/jsSite/src/facade.scala +++ b/jsSite/src/facade.scala @@ -27,7 +27,9 @@ import org.scalajs.dom.Element import scala.util.Random import org.scalajs.dom.Element import org.scalajs.dom.XMLHttpRequest +import scala.annotation.experimental +@experimental() object showJsDocs: def apply(path: String, node: Element, width: Int = 50) = val child = dom.document.createElement("div") diff --git a/site/docs/_docs/benchmarks/sum.mdoc.md b/site/docs/_docs/benchmarks/sum.mdoc.md index cf4d0d1..bf88e19 100644 --- a/site/docs/_docs/benchmarks/sum.mdoc.md +++ b/site/docs/_docs/benchmarks/sum.mdoc.md @@ -18,6 +18,7 @@ And the function left in vexct over time (against regressions) ```scala mdoc:js sc:nocompile import vecxt.plot.* import vecxt.facades.* + showJsDocs.fromSpec(BenchmarkPlots.sumBenchmarkOverTime, node) ``` diff --git a/site/docs/_docs/examples.mdoc.md b/site/docs/_docs/examples.mdoc.md index 06fa443..1b74223 100644 --- a/site/docs/_docs/examples.mdoc.md +++ b/site/docs/_docs/examples.mdoc.md @@ -33,9 +33,9 @@ cosineSimilarity(v1, v2) (v1(v1 <= 2)).printArr ``` -And Ints +And Ints. Note that the API here is more limited at the moment. -```scala mdoc +```scala mdoc:reset import vecxt.all.* import narr.* import vecxt.BoundsCheck.DoBoundsCheck.yes diff --git a/vecxt/js/src/array.scala b/vecxt/js/src/array.scala index 085ac1b..604b133 100644 --- a/vecxt/js/src/array.scala +++ b/vecxt/js/src/array.scala @@ -75,16 +75,6 @@ object arrays: end for result end || - - // def copy: Array[Boolean] = - // val copyOfThisVector: Array[Boolean] = new Array[Boolean](vec.length) - // var i = 0 - // while i < vec.length do - // copyOfThisVector(i) = vec(i) - // i = i + 1 - // end while - // copyOfThisVector - // end copy end extension extension (vec: NArray[Double]) diff --git a/vecxt/jvm/src/arrays.scala b/vecxt/jvm/src/arrays.scala index ab032d4..2fbc286 100644 --- a/vecxt/jvm/src/arrays.scala +++ b/vecxt/jvm/src/arrays.scala @@ -132,6 +132,7 @@ object arrays: end extension extension (vec: Array[Int]) + inline def =:=(num: Int): Array[Boolean] = logicalIdx(VectorOperators.EQ, num) From 07ed251cd425e84fd80064f3210f431eb41d6fa2 Mon Sep 17 00:00:00 2001 From: Simon Parten Date: Thu, 12 Dec 2024 21:10:14 +0100 Subject: [PATCH 5/5] . --- build.mill | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.mill b/build.mill index 156369e..0dbfec2 100644 --- a/build.mill +++ b/build.mill @@ -1,7 +1,7 @@ import mill.scalalib.publish.Scope.Test import $ivy.`com.github.lolgab::mill-crossplatform::0.2.4` -import $ivy.`io.github.quafadas:millSite_mill0.12_2.13:0.0.37-DIRTYafea46f4` +import $ivy.`io.github.quafadas:millSite_mill0.12_2.13:0.0.38` import $ivy.`de.tototec::de.tobiasroeser.mill.vcs.version::0.4.0` import $ivy.`com.lihaoyi::mill-contrib-jmh:` import $ivy.`com.goyeau::mill-scalafix::0.4.2`