diff --git a/src/benchmark/sucoverrelax/actor.ts b/src/benchmark/sucoverrelax/actor.ts new file mode 100644 index 000000000..7beb59489 --- /dev/null +++ b/src/benchmark/sucoverrelax/actor.ts @@ -0,0 +1,64 @@ +/* eslint-disable @typescript-eslint/prefer-readonly */ +// The SOR (Re)actor +import { Reactor, State } from "../../core/internal"; + +export class SORActor extends Reactor { + constructor( + parent: Reactor, + pos: number, + _value: number, + colour: number, + nx: number, + ny: number, + omega: number, + sorSource: Reactor, + peer: boolean + ) { + super(parent, "SORActor"); + + const x = Math.floor(pos / ny); + const y = pos % ny; + + const omegaOverFour = 0.25 * omega; + const oneMinusOmega = 1.0 - omega; + + const neighbours = (() => { + const calPos = (x: number, y: number): number => (x * ny + y); + + if (x > 0 && x < nx - 1 && y > 0 && y < ny - 1) { + return [calPos(x, y + 1), + calPos(x + 1, y), + calPos(x, y - 1), + calPos(x - 1, y)]; + } + if ((x === 0 || x === (nx - 1)) && (y === 0 || y === (ny - 1))) { + return [ + (x === 0) ? calPos(x + 1, y) : calPos(x - 1, y), + (y === 0) ? calPos(x, y + 1) : calPos(x, y - 1) + ]; + } + if ((x === 0 || x === (nx - 1)) || (y === 0 || y === (ny - 1))) { + if (x === 0 || x === nx - 1) { + return [ + (x === 0) ? calPos(x + 1, y) : calPos(x - 1, y), + calPos(x, y + 1), + calPos(x, y - 1) + ]; + } + return [ + (y === 0) ? calPos(x, y + 1) : calPos(x, y - 1), + calPos(x+1, y), + calPos(x-1, y) + ]; + } + return []; + })(); + } + + private iter = new State(0); + private maxIter = new State(0); + private msgRcv = new State(0); + private sorActors = new State([]); + + protected +} \ No newline at end of file diff --git a/src/benchmark/sucoverrelax/peer.ts b/src/benchmark/sucoverrelax/peer.ts new file mode 100644 index 000000000..65f762682 --- /dev/null +++ b/src/benchmark/sucoverrelax/peer.ts @@ -0,0 +1,18 @@ +import { Reactor, State } from "../../core/internal"; +import { SORRunner } from "./runner"; +import { SorBorder } from "./sorutils"; + +export class SORPeer extends Reactor { + sorActors: State; + constructor( + parent: Reactor, + s: number, + partStart: number, + matrixPart: number[][], + border: SorBorder, + sorSource: SORRunner + ) { + super(parent, "SORPeer"); + this.sorActors = new State([]); + } +} \ No newline at end of file diff --git a/src/benchmark/sucoverrelax/runner.ts b/src/benchmark/sucoverrelax/runner.ts new file mode 100644 index 000000000..16e7ab9ad --- /dev/null +++ b/src/benchmark/sucoverrelax/runner.ts @@ -0,0 +1,82 @@ +import { InMultiPort, InPort, OutMultiPort, OutPort, Parameter, Reactor, State, } from "../../core/internal"; +import { SORActor } from "./actor"; +import { SORPeer } from "./peer"; +import { Message, SorBorder, omega } from "./sorutils"; + +export class SORRunner extends Reactor { + protected sorActors: State; + protected sorPeer: State; + + protected portToSORActors: OutPort; + protected portToSORPeer: OutPort; + // Unsure if this would work, let's just try...... + protected portFromSORActor: InPort; + protected portFromSORPeer: InPort; + + constructor(parent: Reactor, size: number, _randoms: number[][]) { + super(parent, "SORRunner"); + // These are in SorRunner; + const s = size; + // In the scala implementation a simple /2 was used. + // In JS we might need to enforce some sort of guarantee as it was used to calculate position + const part = Math.floor(s / 2); + /** These are from Savina. They should be rather irrelevant, actually. */ + this.sorActors = new State([]); + this.sorPeer = new State(undefined); + + /** These are the actual messaging passing mechanism that are synonomous to that of Savina. */ + // This creates a bunch of ports. + this.portToSORActors = new OutPort(this); + this.portToSORPeer = new OutPort(this); + + // SorRunner::boot() + this.addMutation( + [this.startup], + [this.sorActors, this.sorPeer], + function (this, sorActors, sorPeer) { + const myBorder: Reactor[] = []; + const randoms = _randoms; + + // In scala, (i <- 0 until s) is loop excluding s. + const sorActorsValue = sorActors.get(); + for (let i = 0; i < s; ++i) { + let c = i % 2; + for (let j = 0; j < part; ++j) { + const pos = i * (part + 1) + j; + c = 1 - c; + // We modify them in bulk, then update the state. + // Unlike in Scala we do not need to initialise the array here, JS supports sparse array. + // I have absolutely no idea why these parametres are called as such...... + sorActorsValue[pos] = this.getReactor()._uncheckedAddSibling( + SORActor, + pos, randoms[i][j], c, s, part + 1, omega, this.getReactor(), false + ); + // TODO: Make connections + if (j === (part - 1)) { + myBorder[i] = sorActorsValue[pos]; + } + + } + } + sorActors.set(sorActorsValue); + + const partialMatrix: number[][] = []; + for (let i = 0; i < s; ++i) { + for (let j = 0; j < s - part; ++j) { + partialMatrix[i][j] = randoms[i][j + part]; + } + } + + const sorPeerValue = this.getReactor()._uncheckedAddSibling( + SORPeer, + s, part, partialMatrix, new SorBorder(myBorder), + // A dirty hack. Maybe this will be removed as ports get added. + this.getReactor() as SORRunner + ); + sorPeer.set(sorPeerValue); + // TODO: Add connections. + + } + ); + } +} diff --git a/src/benchmark/sucoverrelax/sorutils.ts b/src/benchmark/sucoverrelax/sorutils.ts new file mode 100644 index 000000000..67510ed05 --- /dev/null +++ b/src/benchmark/sucoverrelax/sorutils.ts @@ -0,0 +1,82 @@ +import { Reactor } from "../../core/reactor"; + +// Savina implementation of PRNG +export class SavinaPRNG { + private value: number; + + constructor(value?: number) { + this.value = value ?? 1145141919; + } + + public nextNumber(): number { + this.value = ((this.value * 1309) + 13849) & 65535; + return this.value; + } + + public nextFloat(): number { + return 1.0 / (this.nextNumber() + 1); + } +} + +// This is not a recommended way to use JS, but whatever...... + +export const refVal = [ + 0.000003189420084871275, + 0.001846644602759566, + 0.0032099996270638005, + 0.0050869220175413146, + 0.008496328291240363, + 0.016479973604143234, + 0.026575660248076397, + // This is different from the Savina one because JS doesn't have high precision + 1.026575660248076, + 2.026575660248076, + 3.026575660248076 +]; + +export const jacobi = 100; + +export const omega = 1.25; + +export function randomMatrix(m: number, n: number): number[][] { + const mat = []; + const prng = new SavinaPRNG(114514); + for (let i = 0; i < m; ++i) { + const row = []; + for (let j = 0; j < n; ++j) { + row.push(prng.nextFloat() * 1e-6); + } + mat.push(row); + } + return mat; +} + +export function jgfValidate(gTotal: number, size: number): void { + const dev = Math.abs(gTotal - refVal[size]); + if (dev > 1.0e-12) { + console.log("Validation failed"); + console.log(`GTotal=${gTotal}; ${refVal[size]}; ${dev}; ${size}`); + } else { + console.log("Validation OK!"); + } +} + +export enum MessageTypes { + sorBorderMessage, + sorStartMessage, + sorValueMessage, + sorBootMessage, + sorResuleMessage +} + +export class SorBorder { + borderActors: Reactor[]; + constructor(borderActors: Reactor[]) { + this.borderActors = borderActors; + } +} + +export interface Message { + messageType: MessageTypes; +} + diff --git a/src/benchmark/sucoverrelax/sucoverrelax.ts b/src/benchmark/sucoverrelax/sucoverrelax.ts new file mode 100644 index 000000000..652187c56 --- /dev/null +++ b/src/benchmark/sucoverrelax/sucoverrelax.ts @@ -0,0 +1,20 @@ +import { + type WritablePort, + Parameter, + InPort, + OutPort, + State, + Action, + Reactor, + App, + TimeValue, + Origin, + Log + } from "../../core/internal"; + + + + + + + diff --git a/src/benchmark/sucoverrelax/test.ts b/src/benchmark/sucoverrelax/test.ts new file mode 100644 index 000000000..be109f340 --- /dev/null +++ b/src/benchmark/sucoverrelax/test.ts @@ -0,0 +1,86 @@ +import { App, InPort, OutPort, Reactor } from "../../core/internal"; + +class Master extends Reactor { + outp: OutPort; + + constructor(parent: Reactor, receivers: Receiver[]) { + super(parent, ""); + this.outp = new OutPort(this); + + this.addMutation( + [this.startup], + [this.outp], + function(this, outp) { + let i = 0; + for (const r of receivers) { + console.log(`Master: triggering ${i}`) + this.connect(outp, r.inp); + this.getReactor().writable(outp).set(i++); + this.disconnect(outp, r.inp); + } + } + ); + } +} + +class Receiver extends Reactor { + inp: InPort; + outp: OutPort; + + constructor(parent: Reactor, receiver2: Receiver2) { + super(parent, ""); + this.inp = new InPort(this); + this.outp = new OutPort(this); + + this.addMutation( + [this.inp], + [this.inp, this.outp], + function(this, inp, outp) { + const message = inp.get(); + if (message == null) { + throw Error("Receiver: Message is null."); + } + console.log(`Receiver: message ${message}. Sending.`); + this.connect(outp, receiver2.inp); + this.getReactor().writable(outp).set(message); + this.disconnect(outp, receiver2.inp); + } + ); + } +} + +class Receiver2 extends Reactor { + inp: InPort; + + constructor(parent: Reactor) { + super(parent, ""); + this.inp = new InPort(this); + + this.addReaction( + [this.inp], + [this.inp], + function (this, inp) { + console.log(`Receiver2: received ${inp.get()}`) + } + ); + } +} + +class Apppp extends App { + master: Master; + recvs: Receiver[]; + recv2: Receiver2; + + constructor() { + super(undefined, undefined, false, ()=>(undefined), ()=>(undefined), ""); + this.recv2 = new Receiver2(this); + this.recvs = []; + for (let i = 0; i < 10; ++i) { + this.recvs.push(new Receiver(this, this.recv2)); + } + this.master = new Master(this, this.recvs); + } +} + +const app = new Apppp(); +app._start(); \ No newline at end of file