Skip to content

Commit

Permalink
Merge pull request #3350 from quantified-uncertainty/simpleValueSummary
Browse files Browse the repository at this point in the history
Added summary for simpleValue
  • Loading branch information
OAGr authored Aug 31, 2024
2 parents b33bc87 + aa6af0e commit f126c2d
Show file tree
Hide file tree
Showing 12 changed files with 11,831 additions and 14,281 deletions.
2 changes: 1 addition & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
"rollup-plugin-node-builtins": "^2.1.2",
"storybook": "^8.1.6",
"tailwindcss": "^3.4.3",
"typescript": "^5.5.3",
"typescript": "5.5.3",
"vite": "^5.2.10"
},
"peerDependencies": {
Expand Down
125 changes: 40 additions & 85 deletions packages/components/src/components/ui/NumberShower.tsx
Original file line number Diff line number Diff line change
@@ -1,76 +1,10 @@
import * as React from "react";

import { DurationUnitName, durationUnits } from "@quri/squiggle-lang";

const orderOfMagnitudeNum = (n: number) => {
return Math.pow(10, n);
};

// 105 -> 3
const orderOfMagnitude = (n: number) => {
return Math.floor(Math.log(n) / Math.LN10 + 0.000000001);
};

function withXSigFigs(number: number, sigFigs: number) {
const withPrecision = number.toPrecision(sigFigs);
const formatted = Number(withPrecision);
return `${formatted}`;
}

class NumberShowerBuilder {
number: number;
precision: number;

constructor(number: number, precision = 2) {
this.number = number;
this.precision = precision;
}

convert() {
const number = Math.abs(this.number);
const response = this.evaluate(number);
if (this.number < 0) {
response.value = "-" + response.value;
}
return response;
}

metricSystem(number: number, order: number) {
const newNumber = number / orderOfMagnitudeNum(order);
const precision = this.precision;
return `${withXSigFigs(newNumber, precision)}`;
}

evaluate(number: number) {
if (number === 0) {
return { value: this.metricSystem(0, 0) };
} else if (number === Infinity) {
return { value: "Infinity" };
}

const order = orderOfMagnitude(number);
if (order < -2) {
return { value: this.metricSystem(number, order), power: order };
} else if (order < 4) {
return { value: this.metricSystem(number, 0) };
} else if (order < 6) {
return { value: this.metricSystem(number, 3), symbol: "K" };
} else if (order < 9) {
return { value: this.metricSystem(number, 6), symbol: "M" };
} else if (order < 12) {
return { value: this.metricSystem(number, 9), symbol: "B" };
} else if (order < 15) {
return { value: this.metricSystem(number, 12), symbol: "T" };
} else {
return { value: this.metricSystem(number, order), power: order };
}
}
}

export function numberShow(number: number, precision = 2) {
const ns = new NumberShowerBuilder(number, precision);
return ns.convert();
}
import {
DurationUnitName,
durationUnits,
makeFormattedNumber,
} from "@quri/squiggle-lang";

//At this point we only support duration units.
export interface NumberShowerProps {
Expand All @@ -84,20 +18,41 @@ export const NumberShower: React.FC<NumberShowerProps> = ({
precision = 2,
unitName,
}) => {
const numberWithPresentation = numberShow(number, precision);
return (
<span>
{numberWithPresentation.value}
{numberWithPresentation.symbol}
{numberWithPresentation.power ? (
const numberWithPresentation = makeFormattedNumber(number, {
precision,
});
const negativeSign = numberWithPresentation.isNegative ? "-" : "";
const unitString = unitName && <span> {durationUnits[unitName].plural}</span>;
switch (numberWithPresentation.type) {
case "basic":
return (
<span>
{negativeSign}
{numberWithPresentation.value}
{unitString}
</span>
);
case "scientific":
return (
<span>
{"\u00b7" /* dot symbol */}10
<sup style={{ fontSize: "0.7em" }}>
{numberWithPresentation.power}
</sup>
{negativeSign}
{numberWithPresentation.mantissa}{" "}
<span>
{"\u00b7" /* dot symbol */}10
<sup style={{ fontSize: "0.7em" }}>
{numberWithPresentation.exponent}
</sup>
</span>
{unitString}
</span>
) : null}
{unitName && <span> {durationUnits[unitName].plural}</span>}
</span>
);
);
case "unit":
return (
<span>
{negativeSign}
{numberWithPresentation.mantissa}
{numberWithPresentation.symbol} {unitString}
</span>
);
}
};
2 changes: 1 addition & 1 deletion packages/squiggle-lang/__tests__/SqValue/asJS_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ describe("SqValue.asJS", () => {
await testRun('{ x: 5, y: [3, "foo", { dist: normal(5,2) } ] }')
).result.asJS();

expect((value as any).value.y[2].value.dist).toBeInstanceOf(Array);
expect((value as any).value.y[2].value.dist).toBeInstanceOf(Object);
});
});
2 changes: 1 addition & 1 deletion packages/squiggle-lang/__tests__/reducer/seeds_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ async function getSamplesForSeed(seed?: string | undefined) {
if (!output.ok) {
throw new Error("Run failed");
}
const samples = output.value.result.asJS();
const samples = (output.value.result.asJS() as { samples: number[] }).samples;
if (!Array.isArray(samples)) {
throw new Error("Expected an array");
}
Expand Down
102 changes: 102 additions & 0 deletions packages/squiggle-lang/__tests__/utility/FormattedNumber_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {
FormatterOptions,
makeFormattedNumber,
numberToFormattedNumberString,
} from "../../src/utility/FormattedNumber.js";

describe("FormattedNumber", () => {
const makeTestCases: [number, ReturnType<typeof makeFormattedNumber>][] = [
[123.45, { type: "basic", value: "120", isNegative: false }],
[-123.45, { type: "basic", value: "120", isNegative: true }],
[1234, { type: "basic", value: "1200", isNegative: false }],
[
1234567,
{ type: "unit", mantissa: "1.2", symbol: "M", isNegative: false },
],
[0, { type: "basic", value: "0", isNegative: false }],
[Infinity, { type: "basic", value: "Infinity", isNegative: false }],
[
0.0000001,
{ type: "scientific", mantissa: "1", exponent: -7, isNegative: false },
],
[
1e20,
{ type: "scientific", mantissa: "1", exponent: 20, isNegative: false },
],
[-Infinity, { type: "basic", value: "Infinity", isNegative: true }],
[NaN, { type: "basic", value: "NaN", isNegative: false }],
];

const makeWithOptionsTestCases: [
number,
FormatterOptions,
ReturnType<typeof makeFormattedNumber>,
][] = [
[
123.45,
{ precision: 1 },
{ type: "basic", value: "100", isNegative: false },
],
[
1234567,
{ precision: 3 },
{ type: "unit", mantissa: "1.23", symbol: "M", isNegative: false },
],
[
0.0001,
{ forceScientific: true },
{ type: "scientific", mantissa: "1", exponent: -4, isNegative: false },
],
[
1000,
{ forceScientific: true },
{ type: "basic", value: "1000", isNegative: false },
],
[
1,
{ forceScientific: true },
{ type: "basic", value: "1", isNegative: false },
],
[
9876,
{ precision: 4 },
{ type: "basic", value: "9876", isNegative: false },
],
];

const makeAndToStringTestCases = [
[1, "1"],
[123.45, "120"],
[-123.45, "-120"],
[1234, "1200"],
[1234567, "1.2M"],
[0, "0"],
[Infinity, "Infinity"],
[0.0000001, "1e-7"],
[1e20, "1e20"],
[-Infinity, "-Infinity"],
[NaN, "NaN"],
];

describe("make", () => {
test.each(makeTestCases)("make(%p) -> %p", (input, expected) => {
expect(makeFormattedNumber(input)).toEqual(expected);
});

test.each(makeWithOptionsTestCases)(
"make(%p, %p) -> %p",
(input, options, expected) => {
expect(makeFormattedNumber(input, options)).toEqual(expected);
}
);
});

describe("makeAndToString", () => {
test.each(makeAndToStringTestCases)(
"makeAndToString(%p) -> %p",
(input, expected) => {
expect(numberToFormattedNumberString(input as number)).toBe(expected);
}
);
});
});
106 changes: 106 additions & 0 deletions packages/squiggle-lang/__tests__/utility/compactTreeString_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { compactTreeString } from "../../src/utility/compactTreeString.js";
import {
removeLambdas,
SimpleValue,
simpleValueFromAny,
SimpleValueWithoutLambda,
} from "../../src/value/simpleValue.js";
import { testRun } from "../helpers/helpers.js";

async function runAndSummarize(code: string): Promise<string> {
const result = await testRun(code);
const simpleValue: SimpleValue = simpleValueFromAny(result.result.asJS());
const withoutLambdas: SimpleValueWithoutLambda = removeLambdas(simpleValue);
return compactTreeString(withoutLambdas, {
maxDepth: 4,
maxArrayItems: 4,
maxDictItems: 4,
});
}

describe("compactTreeString", () => {
test("Simple array", () => {
const result = compactTreeString([1, 2, 3, 4, 5]);
expect(result).toBe("[1, 2, 3, 4, 5]");
});

test("Nested array", () => {
const result = compactTreeString([1, [2, 3], 4, [5, 6]]);
expect(result).toBe("[1, [2, 3], 4, [5, 6]]");
});

test("Nested object", async () => {
const result = await runAndSummarize(`{a: {b: 1, c: 2}, d: [3, 4]}`);
expect(result).toBe(`{
vtype: "Dict",
value: {
a: {
vtype: "Dict",
value: {
b: ...,
c: ...
}
},
d: [3, 4]
}
}`);
});

test("Long array", async () => {
const result = await runAndSummarize(
`[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]`
);
expect(result).toBe("[1, 2, 3, 4, ...15 total]");
});

test("Long object (hash)", async () => {
const result = await runAndSummarize(
`{a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15}`
);
expect(result).toBe(`{
vtype: "Dict",
value: {
a: 1,
b: 2,
c: 3,
d: 4,
...15 total
}
}`);
});

test("complex object", async () => {
const result = await runAndSummarize(
`x = {foo: {bar: 1 to 5}}
y = 5000 to 10000
z(f) = f + 3
{x,y,z}`
);
expect(result).toBe(`{
vtype: "Dict",
value: {
x: {
vtype: "Dict",
value: {
foo: ...
}
},
y: {
vType: "SampleSetDist",
samples: [10400, 6830, 5280, 7700, ...1000 total],
summary: {
mean: ...,
p5: ...,
p50: ...,
p95: ...
}
},
z: {
vType: "Lambda",
toString: "(f) => internal code",
paramenterString: "f"
}
}
}`);
});
});
2 changes: 1 addition & 1 deletion packages/squiggle-lang/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"peggy": "^4.0.2",
"prettier": "^3.2.5",
"ts-node": "^10.9.2",
"typescript": "^5.5.3"
"typescript": "5.5.3"
},
"files": [
"dist",
Expand Down
6 changes: 6 additions & 0 deletions packages/squiggle-lang/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,9 @@ export {
export { BaseRunner } from "./runners/BaseRunner.js";

export { getStdLib } from "./library/index.js";

export {
type FormattedNumber,
formattedNumberToString,
makeFormattedNumber,
} from "./utility/FormattedNumber.js";
Loading

0 comments on commit f126c2d

Please sign in to comment.