Compare commits

...

2 Commits

Author SHA1 Message Date
Sam Nolan
6637db4154 Add genType declaration to fix webpack compiling 2022-04-08 17:35:56 +10:00
Sam Nolan
25a6c374d1 Improve Typescript Interface 2022-04-08 17:28:09 +10:00
15 changed files with 187 additions and 45 deletions

View File

@ -1,34 +1,16 @@
import { run } from '../src/js/index';
import { run, exportToString } from '../src/js/index';
let testRun = (x: string) => {
let result = run(x)
if(result.tag == 'Ok'){
return { tag: 'Ok', value: result.value.exports }
}
else {
return result
}
}
let testRun = (expression: string): string => exportToString(run(expression)[0])
let runTest = (expression: string, expected: string) => test(expression, () => expect(testRun(expression)).toEqual(expected))
let runErrorTest = (expression: string, expected: string) => test(`${expression} will error`, () => expect(() => testRun(expression)).toThrowError(expected))
describe("Simple calculations and results", () => {
test("mean(normal(5,2))", () => {
expect(testRun("mean(normal(5,2))")).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 5 } ] })
})
test("10+10", () => {
let foo = testRun("10 + 10")
expect(foo).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 20 } ] })
})
})
describe("Log function", () => {
test("log(1) = 0", () => {
let foo = testRun("log(1)")
expect(foo).toEqual({ tag: 'Ok', value: [ { NAME: 'Float', VAL: 0} ]})
})
runTest("mean(normal(5,2))", "5")
runTest("10 + 10", "20")
})
describe("Multimodal too many weights error", () => {
test("mm(0,0,[0,0,0])", () => {
let foo = testRun("mm(0,0,[0,0,0])")
expect(foo).toEqual({ "tag": "Error", "value": "Function multimodal error: Too many weights provided" })
})
});
describe("Log function", () => {
runTest("log(1)", "0")
})

View File

@ -4,7 +4,7 @@ open Jest
open Expect
let expectEvalToBe = (expr: string, answer: string) =>
Reducer.eval(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
describe("builtin", () => {
// All MathJs operators and functions are available for string, number and boolean

View File

@ -8,7 +8,7 @@ let expectParseToBe = (expr: string, answer: string) =>
Reducer.parse(expr)->Expression.toStringResult->expect->toBe(answer)
let expectEvalToBe = (expr: string, answer: string) =>
Reducer.eval(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
// Current configuration does not ignore this file so we have to have a test
test("test helpers", () => expect(1)->toBe(1))

View File

@ -33,7 +33,9 @@
"gentypeconfig": {
"language": "typescript",
"module": "commonjs",
"shims": {},
"shims": {
"Js": "Js"
},
"debug": {
"all": false,
"basic": false

View File

@ -1,7 +1,12 @@
import {runAll} from '../rescript/ProgramEvaluator.gen';
import type { Inputs_SamplingInputs_t as SamplingInputs, exportEnv, exportType, exportDistribution} from '../rescript/ProgramEvaluator.gen';
export type { SamplingInputs, exportEnv, exportDistribution }
export type {t as DistPlus} from '../rescript/OldInterpreter/DistPlus.gen';
import * as _ from 'lodash';
import {evaluate} from '../rescript/Reducer/Reducer.gen';
import type { expressionValue } from '../rescript/Reducer/Reducer_Expression/Reducer_Expression.gen';
import type { pointSetDist } from '../rescript/Distributions/PointSetDist/PointSetTypes.gen';
import type { genericDist } from '../rescript/Distributions/GenericDist/GenericDist_Types.gen';
import { errorToString } from '../rescript/Reducer/Reducer_ErrorValue.gen';
import { toPointSet, inv } from '../rescript/Distributions/GenericDist/GenericDist.gen';
import type { Inputs_SamplingInputs_t as SamplingInputs, exportEnv, exportDistribution} from '../rescript/ProgramEvaluator.gen';
export type { SamplingInputs, exportEnv, exportDistribution, tsExport }
export let defaultSamplingInputs : SamplingInputs = {
sampleCount : 10000,
@ -9,9 +14,140 @@ export let defaultSamplingInputs : SamplingInputs = {
pointDistLength : 1000
}
export function run(squiggleString : string, samplingInputs? : SamplingInputs, environment?: exportEnv) : { tag: "Ok"; value: exportType }
| { tag: "Error"; value: string } {
let si : SamplingInputs = samplingInputs ? samplingInputs : defaultSamplingInputs
let env : exportEnv = environment ? environment : []
return runAll(squiggleString, si, env)
type taggedOption<tag,value> = {
tag: tag,
value: value
}
function tagOption<T, V>(tag : T, value: V): taggedOption<T,V> {
return { tag: tag, value: value};
}
type tsExport = taggedOption<"string", string> | taggedOption<"symbol", string> | taggedOption<"number", number> | taggedOption<"boolean", boolean> | taggedOption<"distribution", Distribution> | taggedOption<"array", tsExport[]> | taggedOption<"record", {[key: string]: tsExport}> | taggedOption<"function", (x: number) => tsExport>
// This is here mainly for testing purposes
export function exportToString(result : tsExport) : string{
if(result.tag === "string"){
return `"${result.value}"`
}
else if(result.tag === "boolean"){
return `${result.value}`
}
else if(result.tag === "array"){
return `[${result.value.map(exportToString).join(", ")}]`
}
else if(result.tag === "distribution"){
return result.value.toString()
}
else if(result.tag === "number"){
return `${result.value}`
}
else if(result.tag === "symbol"){
return `${result.value}`
}
else if(result.tag === "record"){
return `${_.mapValues(result.value, exportToString)}`
}
}
function expressionValueToValue(value : expressionValue, samplingInputs: SamplingInputs) : tsExport {
if(value.tag == "EvArray"){
return tagOption("array", value.value.map((val) => expressionValueToValue(val,samplingInputs)));
}else if (value.tag == "EvBool"){
return tagOption("boolean", value.value)
}
else if(value.tag == "EvDistribution"){
return tagOption("distribution", new Distribution(value.value, samplingInputs));
}
else if(value.tag == "EvNumber"){
return tagOption("number", value.value);
}
else if(value.tag == "EvString"){
return tagOption("string", value.value);
}
else if(value.tag == "EvSymbol"){
return tagOption("symbol", value.value);
}
else if(value.tag == "EvRecord"){
return tagOption("record", _.mapValues(value.value, (val) => expressionValueToValue(val, samplingInputs)));
}
}
export function run(squiggleString : string, samplingInputs : SamplingInputs = defaultSamplingInputs, _environment?: exportEnv): tsExport[] {
let result = evaluate(squiggleString);
if(result.tag == "Ok"){
return [expressionValueToValue(result.value, samplingInputs)];
}
else{
throw Error(errorToString(result.value));
}
}
class Distribution {
dist: genericDist
samplingInputs : SamplingInputs
constructor(dist : genericDist, samplingInputs: SamplingInputs){
this.dist = dist;
this.samplingInputs = samplingInputs;
}
pointShape() {
let pointSet = toPointSet({xyPointLength: this.samplingInputs.outputXYPoints, sampleCount: this.samplingInputs.sampleCount}, this.dist)
if(pointSet.tag == "Ok"){
return new Shape(pointSet.value);
}
}
inv(x: number): number{
let result= inv(this.dist, x)
if (result.tag == "Ok") {
return result.value;
}
else {
throw Error(result.value.toString())
}
}
toString(): string {
return "Todo"
}
}
type point = { x: number, y: number, cdf: number}
class Shape {
shape : pointSetDist
constructor(shape : pointSetDist){
this.shape = shape;
}
discretePoints(): point[]{
let discreteShape = undefined;
if(this.shape.tag == "Discrete"){
discreteShape = this.shape.value;
} else if (this.shape.tag == "Mixed"){
discreteShape = this.shape.value.discrete;
}
if(discreteShape !== undefined) {
return _.zipWith(discreteShape.xyShape.xs, discreteShape.xyShape.ys, discreteShape.integralCache.xyShape.ys, (x, y, c) => ({x, y, cdf: c}))
}
else {
return []
}
}
continuousPoints(): point[]{
let continuousShape = undefined;
console.log(this.shape.tag)
if(this.shape.tag == "Continuous"){
continuousShape = this.shape.value;
} else if (this.shape.tag == "Mixed"){
continuousShape = this.shape.value.continuous;
}
if(continuousShape !== undefined) {
return _.zipWith(continuousShape.xyShape.xs, continuousShape.xyShape.ys, continuousShape.integralCache.xyShape.ys, (x, y, c) => ({x, y, cdf: c}))
}
else {
return []
}
}
}

View File

@ -13,6 +13,13 @@ let sampleN = (t: t, n) =>
| SampleSet(_) => Error(GenericDist_Types.NotYetImplemented)
}
let inv = (t: t, x: float): result<float, error> =>
switch t {
| PointSet(r) => Ok(PointSetDist.inv(x, r))
| Symbolic(r) => Ok(SymbolicDist.T.inv(x, r))
| SampleSet(_) => Error(GenericDist_Types.NotYetImplemented)
}
let fromFloat = (f: float): t => Symbolic(SymbolicDist.Float.make(f))
let toString = (t: t) =>

View File

@ -7,6 +7,9 @@ type pointwiseAddFn = (t, t) => result<t, error>
let sampleN: (t, int) => result<array<float>, error>
@genType
let inv : (t, float) => result<float, error>
let fromFloat: float => t
let toString: t => string
@ -19,6 +22,7 @@ let toFloatOperation: (
~distToFloatOperation: Operation.distToFloatOperation,
) => result<float, error>
@genType
let toPointSet: (
~xyPointLength: int,
~sampleCount: int,

View File

@ -1,8 +1,10 @@
@genType
type genericDist =
| PointSet(PointSetTypes.pointSetDist)
| SampleSet(SampleSet.t)
| Symbolic(SymbolicDistTypes.symbolicDist)
@genType
type error =
| NotYetImplemented
| Unreachable

View File

@ -1,3 +1,4 @@
@genType
type t = array<float>
// TODO: Refactor to raise correct error when not enough samples
@ -142,4 +143,4 @@ let toPointSetDist = (
}
samplesParse
}
}

View File

@ -5,5 +5,5 @@ module Extra = Reducer_Extra
module Js = Reducer_Js
module MathJs = Reducer_MathJs
let eval = Expression.eval
let evaluate = Expression.eval
let parse = Expression.parse

View File

@ -1,8 +1,11 @@
module Dispatch = Reducer_Dispatch
@genType
module ErrorValue = Reducer_ErrorValue
@genType
module Expression = Reducer_Expression
module Extra = Reducer_Extra
module Js = Reducer_Js
module MathJs = Reducer_MathJs
let eval: string => result<Expression.expressionValue, ErrorValue.errorValue>
@genType
let evaluate: string => result<Reducer_Expression.expressionValue, Reducer_ErrorValue.errorValue>
let parse: string => result<Expression.expression, ErrorValue.errorValue>

View File

@ -1,3 +1,4 @@
@genType
type errorValue =
| REArrayIndexNotFound(string, int)
| REFunctionExpected(string)
@ -7,6 +8,7 @@ type errorValue =
type t = errorValue
@genType
let errorToString = err =>
switch err {
| REArrayIndexNotFound(msg, index) => `${msg}: ${Js.String.make(index)}`

View File

@ -1,7 +1,8 @@
module Result = Belt.Result
module T = Reducer_Expression_T
type expression = T.expression
type expressionValue = ReducerInterface.ExpressionValue.expressionValue
@genType
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
type t = expression
let toString: T.expression => Js.String.t
let toStringResult: result<T.expression, 'a> => string

View File

@ -5,6 +5,7 @@
module Extra_Array = Reducer_Extra_Array
module ErrorValue = Reducer_ErrorValue
@genType
type rec expressionValue =
| EvBool(bool)
| EvNumber(float)

View File

@ -0,0 +1 @@
export type Dict_t<T> = {[key: string]: T}