diff --git a/lang-trials/scala-benchmark/.gitignore b/lang-trials/scala-benchmark/.gitignore new file mode 100644 index 0000000..9f2a453 --- /dev/null +++ b/lang-trials/scala-benchmark/.gitignore @@ -0,0 +1,2 @@ +target +sbt.json \ No newline at end of file diff --git a/lang-trials/scala-benchmark/build.sbt b/lang-trials/scala-benchmark/build.sbt new file mode 100644 index 0000000..507885d --- /dev/null +++ b/lang-trials/scala-benchmark/build.sbt @@ -0,0 +1,9 @@ +enablePlugins(ScalaJSPlugin) + +name := "Circular Law" +scalaVersion := "3.4.2" +scalaJSUseMainModuleInitializer := true + +libraryDependencies += "com.raquo" %%% "laminar" % "17.0.0" +libraryDependencies += "ai.dragonfly" %%% "slash" % "0.3.1" +libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.8.0" diff --git a/lang-trials/scala-benchmark/index.html b/lang-trials/scala-benchmark/index.html new file mode 100644 index 0000000..f9eb11c --- /dev/null +++ b/lang-trials/scala-benchmark/index.html @@ -0,0 +1,10 @@ + + + + + The circular law + + + + + diff --git a/lang-trials/scala-benchmark/main.css b/lang-trials/scala-benchmark/main.css new file mode 100644 index 0000000..a89c3bc --- /dev/null +++ b/lang-trials/scala-benchmark/main.css @@ -0,0 +1,22 @@ +body { + margin-left: 20px; + margin-top: 20px; + color: #fcfcfc; + background-color: #202020; +} + +#app { + display: flex; + flex-direction: column; + width: 600px; +} + +canvas { + float: left; + background-color: #020202; + border-radius: 10px; +} + +input { + margin-top: 5px; +} \ No newline at end of file diff --git a/lang-trials/scala-benchmark/project/build.properties b/lang-trials/scala-benchmark/project/build.properties new file mode 100644 index 0000000..ee4c672 --- /dev/null +++ b/lang-trials/scala-benchmark/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.10.1 diff --git a/lang-trials/scala-benchmark/project/plugins.sbt b/lang-trials/scala-benchmark/project/plugins.sbt new file mode 100644 index 0000000..e5b2699 --- /dev/null +++ b/lang-trials/scala-benchmark/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") diff --git a/lang-trials/scala-benchmark/src/main/scala/CircularLawApp.scala b/lang-trials/scala-benchmark/src/main/scala/CircularLawApp.scala new file mode 100644 index 0000000..2f74cd3 --- /dev/null +++ b/lang-trials/scala-benchmark/src/main/scala/CircularLawApp.scala @@ -0,0 +1,91 @@ +import com.raquo.laminar.api.L.{*, given} +import narr.* +import org.scalajs.dom +import org.scalajs.dom.document +import scala.math.{cos, sin} +import slash.matrix.Matrix +import slash.matrix.decomposition.Eigen + +object CircularLawApp: + val canvas = canvasTag(widthAttr := 600, heightAttr := 600) + val ctx = canvas.ref.getContext("2d").asInstanceOf[dom.CanvasRenderingContext2D] + + val (eigvalSeries, runTimeReport) = randomEigvalSeries[60]() + val timeVar = Var("0") + + def draw(timeStr: String): Unit = + // center and normalize the coordinate system + val width = canvas.ref.width + val height = canvas.ref.height + ctx.setTransform(1d, 0d, 0d, -1d, 0.5*width, 0.5*height) + + // clear the previous frame + ctx.clearRect(-0.5*width, -0.5*width, width, height) + + // find the resolution + val rDisp: Double = 1.5 + val res = width / (2*rDisp) + + // draw the eigenvalues + val eigvals = eigvalSeries(timeStr.toInt) + for n <- 0 to eigvals(0).length-1 do + ctx.beginPath() + ctx.arc( + res * eigvals(0)(n), + res * eigvals(1)(n), + 3d, + 0d, 2*math.Pi + ) + ctx.fill() + + def eigvalsRotated[N <: Int](A: Matrix[N, N], time: Double)(using ValueOf[N]): (NArray[Double], NArray[Double]) = + // create transformation + val maxFreq = 4 + val T = Matrix.identity[N, N] + val dim: Int = valueOf[N] + for n <- 0 to dim by 2 do + val a = cos(math.Pi * time * (n % maxFreq)) + val b = sin(math.Pi * time * (n % maxFreq)) + T(n, n) = a + T(n+1, n) = b + T(n, n+1) = -b + T(n+1, n+1) = a + + // find eigenvalues + val eigen = Eigen(T*A) + ( + eigen.realEigenvalues.asInstanceOf[NArray[Double]], + eigen.imaginaryEigenvalues.asInstanceOf[NArray[Double]] + ) + + def randomEigvalSeries[N <: Int]()(using ValueOf[N]): (List[(NArray[Double], NArray[Double])], String) = + val timeRes = 100 + val dim: Int = valueOf[N] + val startTime = System.currentTimeMillis() + val A = new Matrix[N, N]( + NArray.tabulate(dim*dim)(k => (math.E*k*k) % 2 - 1) + ).times(math.sqrt(3d / dim)) + val series = List.tabulate(timeRes)(t => eigvalsRotated(A, t.toDouble / timeRes)) + val endTime = System.currentTimeMillis() + val runTime = endTime - startTime + (series, runTime.toString() + " ms") + + def main(args: Array[String]): Unit = + ctx.fillStyle = "white" + + lazy val app = div( + idAttr := "app", + div(runTimeReport), + canvas, + input( + typ := "range", + minAttr := "0", + maxAttr := (eigvalSeries.length-1).toString, + controlled( + value <-- timeVar.signal, + onInput.mapToValue --> timeVar.writer + ), + timeVar.signal --> draw + ) + ) + renderOnDomContentLoaded(document.body, app)