Skip to content

Commit 8f2add5

Browse files
CopilotQuafadas
andauthored
Add float array operations across JVM (SIMD), JS, and Native platforms (#66)
* Initial plan * Add comprehensive float array operations for JVM (SIMD), JS, and Native platforms Agent-Logs-Url: https://github.com/Quafadas/vecxt/sessions/13e4bb2a-657d-47a6-a532-f7a905bd6a42 Co-authored-by: Quafadas <24899792+Quafadas@users.noreply.github.com> * Fix -=scalar tail loop bug in float JVM implementation and add coverage test Agent-Logs-Url: https://github.com/Quafadas/vecxt/sessions/13e4bb2a-657d-47a6-a532-f7a905bd6a42 Co-authored-by: Quafadas <24899792+Quafadas@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Quafadas <24899792+Quafadas@users.noreply.github.com>
1 parent 1a25efb commit 8f2add5

8 files changed

Lines changed: 2632 additions & 0 deletions

File tree

vecxt/src-js-native/floatarray.scala

Lines changed: 554 additions & 0 deletions
Large diffs are not rendered by default.

vecxt/src-js/array.scala

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ end arrayUtil
1616

1717
object arrays:
1818
export vecxt.JsNativeDoubleArrays.*
19+
export vecxt.JsNativeFloatArrays.*
1920

2021
extension (d: Float64Array) def printArr: String = d.mkString("[", ",", "]")
2122
end extension
@@ -491,6 +492,252 @@ object arrays:
491492
// val t = js.Math.max( vec.toArray: _* )
492493
end extension
493494

495+
extension (vec: Array[Float])
496+
497+
inline def apply(index: Array[Boolean])(using inline boundsCheck: BoundsCheck.BoundsCheck): Array[Float] =
498+
dimCheck(vec, index)
499+
val trues = index.trues
500+
val newVec = Array.ofDim[Float](trues)
501+
var j = 0
502+
for i <- 0 until index.length do
503+
if index(i) then
504+
newVec(j) = vec(i)
505+
j = 1 + j
506+
end for
507+
newVec
508+
end apply
509+
510+
inline def mean: Float =
511+
var sum = 0.0
512+
var i = 0
513+
while i < vec.length do
514+
sum += vec(i)
515+
i += 1
516+
end while
517+
(sum / vec.length).toFloat
518+
end mean
519+
520+
inline def variance: Float = variance(VarianceMode.Population)
521+
522+
inline def variance(mode: VarianceMode): Float =
523+
meanAndVariance(mode).variance
524+
end variance
525+
526+
inline def meanAndVariance: (mean: Float, variance: Float) =
527+
meanAndVariance(VarianceMode.Population)
528+
529+
inline def meanAndVariance(mode: VarianceMode): (mean: Float, variance: Float) =
530+
var mean = 0.0
531+
var m2 = 0.0
532+
var i = 0
533+
while i < vec.length do
534+
val n = i + 1
535+
val delta = vec(i).toDouble - mean
536+
mean += delta / n
537+
val delta2 = vec(i).toDouble - mean
538+
m2 += delta * delta2
539+
i += 1
540+
end while
541+
val denom = mode match
542+
case VarianceMode.Population => vec.length.toDouble
543+
case VarianceMode.Sample => (vec.length - 1).toDouble
544+
545+
(mean.toFloat, (m2 / denom).toFloat)
546+
end meanAndVariance
547+
548+
inline def std: Float = std(VarianceMode.Population)
549+
550+
inline def std(mode: VarianceMode): Float =
551+
Math.sqrt(vec.variance(mode).toDouble).toFloat
552+
553+
inline def stdDev: Float = stdDev(VarianceMode.Population)
554+
555+
inline def stdDev(mode: VarianceMode): Float = std(mode)
556+
557+
inline def dot(v1: Array[Float])(using inline boundsCheck: BoundsCheck): Float =
558+
dimCheck(vec, v1)
559+
var product = 0.0
560+
var i = 0
561+
while i < vec.length do
562+
product = product + vec(i) * v1(i)
563+
i = i + 1
564+
end while
565+
product.toFloat
566+
end dot
567+
568+
inline def norm: Float =
569+
Math.sqrt(vec.dot(vec)(using vecxt.BoundsCheck.DoBoundsCheck.no).toDouble).toFloat
570+
end norm
571+
572+
inline def +(d: Float): Array[Float] =
573+
vec.clone().tap(_ += d)
574+
575+
inline def +=(d: Float): Unit =
576+
var i = 0
577+
while i < vec.length do
578+
vec(i) = vec(i) + d
579+
i = i + 1
580+
end while
581+
end +=
582+
583+
inline def -(d: Float): Array[Float] =
584+
vec.clone().tap(_ -= d)
585+
end -
586+
587+
inline def -=(d: Float): Unit =
588+
var i = 0
589+
while i < vec.length do
590+
vec(i) = vec(i) - d
591+
i = i + 1
592+
end while
593+
end -=
594+
595+
inline def -(vec2: Array[Float])(using inline boundsCheck: BoundsCheck.BoundsCheck): Array[Float] =
596+
dimCheck(vec, vec2)
597+
vec.clone().tap(_ -= vec2)
598+
end -
599+
600+
inline def -=(vec2: Array[Float])(using inline boundsCheck: BoundsCheck.BoundsCheck): Unit =
601+
dimCheck(vec, vec2)
602+
var i = 0
603+
while i < vec.length do
604+
vec(i) = vec(i) - vec2(i)
605+
i = i + 1
606+
end while
607+
end -=
608+
609+
inline def +(vec2: Array[Float])(using inline boundsCheck: BoundsCheck.BoundsCheck): Array[Float] =
610+
dimCheck(vec, vec2)
611+
vec.clone().tap(_ += vec2)
612+
end +
613+
614+
inline def +=(vec2: Array[Float])(using inline boundsCheck: BoundsCheck.BoundsCheck): Unit =
615+
dimCheck(vec, vec2)
616+
var i = 0
617+
while i < vec.length do
618+
vec(i) = vec(i) + vec2(i)
619+
i = i + 1
620+
end while
621+
end +=
622+
623+
inline def +:+(d: Float): Array[Float] =
624+
vec.clone().tap(_ +:+= d)
625+
end +:+
626+
627+
inline def +:+=(d: Float): Unit =
628+
var i = 0
629+
while i < vec.length do
630+
vec(i) = vec(i) + d
631+
i = i + 1
632+
end while
633+
end +:+=
634+
635+
inline def *=(d: Float): Unit =
636+
var i = 0
637+
while i < vec.length do
638+
vec(i) = vec(i) * d
639+
i = i + 1
640+
end while
641+
end *=
642+
643+
inline def *(d: Float): Array[Float] =
644+
vec.clone().tap(_ *= d)
645+
end *
646+
647+
inline def /=(d: Float): Unit =
648+
var i = 0
649+
while i < vec.length do
650+
vec(i) = vec(i) / d
651+
i = i + 1
652+
end while
653+
end /=
654+
655+
inline def /(d: Float): Array[Float] =
656+
vec.clone().tap(_ /= d)
657+
end /
658+
659+
inline def pearsonCorrelationCoefficient(thatVector: Array[Float])(using
660+
inline boundsCheck: BoundsCheck.BoundsCheck
661+
): Float =
662+
dimCheck(vec, thatVector)
663+
val n = vec.length
664+
var i = 0
665+
666+
var sum_x = 0.0
667+
var sum_y = 0.0
668+
var sum_xy = 0.0
669+
var sum_x2 = 0.0
670+
var sum_y2 = 0.0
671+
672+
while i < n do
673+
sum_x = sum_x + vec(i)
674+
sum_y = sum_y + thatVector(i)
675+
sum_xy = sum_xy + vec(i) * thatVector(i)
676+
sum_x2 = sum_x2 + vec(i) * vec(i)
677+
sum_y2 = sum_y2 + thatVector(i) * thatVector(i)
678+
i = i + 1
679+
end while
680+
((n * sum_xy - (sum_x * sum_y)) / Math.sqrt(
681+
(sum_x2 * n - sum_x * sum_x) * (sum_y2 * n - sum_y * sum_y)
682+
)).toFloat
683+
end pearsonCorrelationCoefficient
684+
685+
inline def spearmansRankCorrelation(thatVector: Array[Float])(using
686+
inline boundsCheck: BoundsCheck.BoundsCheck
687+
): Float =
688+
dimCheck(vec, thatVector)
689+
val theseRanks = vec.elementRanks
690+
val thoseRanks = thatVector.elementRanks
691+
theseRanks.pearsonCorrelationCoefficient(thoseRanks)
692+
end spearmansRankCorrelation
693+
694+
inline def corr(thatVector: Array[Float])(using inline boundsCheck: BoundsCheck.BoundsCheck): Float =
695+
pearsonCorrelationCoefficient(thatVector)
696+
697+
def elementRanks: Array[Float] =
698+
val indexed1 = vec.zipWithIndex
699+
val indexed = indexed1.toArray.sorted(using Ordering.by(_._1))
700+
701+
val ranks: Array[Float] = new Array(vec.length)
702+
ranks(indexed.last._2) = vec.length.toFloat
703+
var currentValue: Float = indexed(0)._1
704+
var r0: Int = 0
705+
var rank: Int = 1
706+
while rank < vec.length do
707+
val temp: Float = indexed(rank)._1
708+
val end: Int =
709+
if temp != currentValue then rank
710+
else if rank == vec.length - 1 then rank + 1
711+
else -1
712+
if end > -1 then
713+
val avg: Float = ((1.0 + (end + r0)) / 2.0).toFloat
714+
var i: Int = r0;
715+
while i < end do
716+
ranks(indexed(i)._2) = avg
717+
i += 1
718+
end while
719+
r0 = rank
720+
currentValue = temp
721+
end if
722+
rank += 1
723+
end while
724+
ranks
725+
end elementRanks
726+
727+
def covariance(thatVector: Array[Float]): Float =
728+
val μThis = vec.mean
729+
val μThat = thatVector.mean
730+
var cv: Double = 0
731+
var i: Int = 0
732+
while i < vec.length do
733+
cv += (vec(i) - μThis) * (thatVector(i) - μThat)
734+
i += 1
735+
end while
736+
(cv / (vec.length - 1)).toFloat
737+
end covariance
738+
739+
end extension
740+
494741
extension (vec: Array[Array[Double]])
495742
inline def horizontalSum: Array[Double] =
496743
val out = Array.ofDim[Double](vec.head.length)

vecxt/src-js/dimCheck.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,10 @@ protected[vecxt] object dimCheck:
3838
inline def apply(a: Array[Int], b: Array[Boolean])(using inline doCheck: BoundsCheck) =
3939
inline if doCheck then if a.length != b.length then throw VectorDimensionMismatch(a.length, b.length)
4040

41+
inline def apply(a: Array[Float], b: Array[Float])(using inline doCheck: BoundsCheck) =
42+
inline if doCheck then if a.length != b.length then throw VectorDimensionMismatch(a.length, b.length)
43+
44+
inline def apply(a: Array[Float], b: Array[Boolean])(using inline doCheck: BoundsCheck) =
45+
inline if doCheck then if a.length != b.length then throw VectorDimensionMismatch(a.length, b.length)
46+
4147
end dimCheck

0 commit comments

Comments
 (0)