Minor refactors

This commit is contained in:
Ozzie Gooen 2022-05-15 15:21:00 -04:00
parent b015c20fa4
commit 91ccd333e0
2 changed files with 117 additions and 18 deletions

View File

@ -64,42 +64,22 @@ type percentiles = {
p99: number;
}[];
export const FunctionChart: React.FC<FunctionChartProps> = ({
fn,
chartSettings,
environment,
}: FunctionChartProps) => {
let [mouseOverlay, setMouseOverlay] = React.useState(0);
function handleHover(_name: string, value: unknown) {
setMouseOverlay(value as number);
}
function handleOut() {
setMouseOverlay(NaN);
}
const signalListeners = { mousemove: handleHover, mouseout: handleOut };
let mouseItem: result<squiggleExpression, errorValue> = !!mouseOverlay
? runForeign(fn, [mouseOverlay], { sampleCount: 10000, xyPointLength: 1000 })
: { tag: "Error", value: { tag: "REExpectedType", value: "Expected float, got NaN" } };
let showChart =
mouseItem.tag === "Ok" && mouseItem.value.tag == "distribution" ? (
<DistributionChart
distribution={mouseItem.value.value}
width={400}
height={140}
showSummary={false}
/>
) : (
<></>
);
type errors = _.Dictionary<
{
x: number;
value: string;
}[]
>;
type point = { x: number; value: result<Distribution, string> };
let getPercentiles = ({ chartSettings, fn, environment }) => {
let data1 = _rangeByCount(
chartSettings.start,
chartSettings.stop,
chartSettings.count
);
type point = { x: number; value: result<Distribution, string> };
let getPercentiles: () => percentiles = () => {
let valueData:any = data1.map((x) => {
let valueData: point[] = data1.map((x) => {
let result = runForeign(fn, [x], environment);
if (result.tag === "Ok") {
if (result.value.tag == "distribution") {
@ -133,8 +113,11 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
}
return acc;
}, initialPartition);
let groupedErrors: errors = _.groupBy(errors, (x) => x.value);
let percentiles: percentiles = functionImage.map(({ x, value }) => {
// We convert it to to a pointSet distribution first, so that in case its a sample set
// distribution, it doesn't internally convert it to a pointSet distribution for every
// single inv() call.
let toPointSet: Distribution = unwrap(value.toPointSet());
return {
x: x,
@ -153,19 +136,134 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
p99: unwrap(toPointSet.inv(0.99)),
};
});
return percentiles;
return { percentiles, errors: groupedErrors };
};
let _getPercentiles = React.useMemo(getPercentiles, [environment, fn])
export const FunctionChart: React.FC<FunctionChartProps> = ({
fn,
chartSettings,
environment,
}: FunctionChartProps) => {
let [mouseOverlay, setMouseOverlay] = React.useState(0);
function handleHover(_name: string, value: unknown) {
setMouseOverlay(value as number);
}
function handleOut() {
setMouseOverlay(NaN);
}
const signalListeners = { mousemove: handleHover, mouseout: handleOut };
let mouseItem: result<squiggleExpression, errorValue> = !!mouseOverlay
? runForeign(fn, [mouseOverlay], {
sampleCount: 10000,
xyPointLength: 1000,
})
: {
tag: "Error",
value: { tag: "REExpectedType", value: "Expected float, got NaN" },
};
let showChart =
mouseItem.tag === "Ok" && mouseItem.value.tag == "distribution" ? (
<DistributionChart
distribution={mouseItem.value.value}
width={400}
height={140}
showSummary={false}
/>
) : (
<></>
);
let getPercentiles2: () => {
percentiles: percentiles;
errors: errors;
} = () => {
let data1 = _rangeByCount(
chartSettings.start,
chartSettings.stop,
chartSettings.count
);
let valueData: point[] = data1.map((x) => {
let result = runForeign(fn, [x], environment);
if (result.tag === "Ok") {
if (result.value.tag == "distribution") {
return { x, value: { tag: "Ok", value: result.value.value } };
} else {
return {
x,
value: {
tag: "Error",
value:
"Cannot currently render functions that don't return distributions",
},
};
}
} else {
return {
x,
value: { tag: "Error", value: errorValueToString(result.value) },
};
}
});
let initialPartition: [
{ x: number; value: Distribution }[],
{ x: number; value: string }[]
] = [[], []];
let [functionImage, errors] = valueData.reduce((acc, current) => {
if (current.value.tag === "Ok") {
acc[0].push({ x: current.x, value: current.value.value });
} else {
acc[1].push({ x: current.x, value: current.value.value });
}
return acc;
}, initialPartition);
let groupedErrors: errors = _.groupBy(errors, (x) => x.value);
let percentiles: percentiles = functionImage.map(({ x, value }) => {
// We convert it to to a pointSet distribution first, so that in case its a sample set
// distribution, it doesn't internally convert it to a pointSet distribution for every
// single inv() call.
let toPointSet: Distribution = unwrap(value.toPointSet());
return {
x: x,
p1: unwrap(toPointSet.inv(0.01)),
p5: unwrap(toPointSet.inv(0.05)),
p10: unwrap(toPointSet.inv(0.12)),
p20: unwrap(toPointSet.inv(0.2)),
p30: unwrap(toPointSet.inv(0.3)),
p40: unwrap(toPointSet.inv(0.4)),
p50: unwrap(toPointSet.inv(0.5)),
p60: unwrap(toPointSet.inv(0.6)),
p70: unwrap(toPointSet.inv(0.7)),
p80: unwrap(toPointSet.inv(0.8)),
p90: unwrap(toPointSet.inv(0.9)),
p95: unwrap(toPointSet.inv(0.95)),
p99: unwrap(toPointSet.inv(0.99)),
};
});
return { percentiles, errors: groupedErrors };
};
let _getPercentiles = React.useMemo(getPercentiles2, [environment, fn]);
return (
<>
<SquigglePercentilesChart
data={{ facet: _getPercentiles }}
data={{ facet: _getPercentiles.percentiles }}
actions={false}
signalListeners={signalListeners}
/>
{showChart}
{_.entries(_getPercentiles.errors).map(([errorName, errorPoints]) => (
<ErrorBox key={errorName} heading={errorName}>
Values:{" "}
{errorPoints
.map((r, i) => <NumberShower key={i} number={r.x} />)
.reduce((a, b) => (
<>
{a}, {b}
</>
))}
</ErrorBox>
))}
</>
);
};

View File

@ -10,7 +10,6 @@ import {
jsImports,
defaultImports,
defaultBindings,
defaultEnvironment,
} from "@quri/squiggle-lang";
import { NumberShower } from "./NumberShower";
import { DistributionChart } from "./DistributionChart";
@ -194,7 +193,7 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
<FunctionChart
fn={expression.value}
chartSettings={chartSettings}
environment={{ sampleCount: environment.sampleCount / 10, xyPointLength: environment.sampleCount / 10 }}
environment={{ sampleCount: environment.sampleCount / 10, xyPointLength: environment.xyPointLength / 10 }}
/>
);
}
@ -233,6 +232,8 @@ const ChartWrapper = styled.div`
`;
let defaultChartSettings = { start: 0, stop: 10, count: 20 };
let defaultEnvironment = { sampleCount: 10000, xyPointLength: 1000 };
export const SquiggleChart: React.FC<SquiggleChartProps> = ({
squiggleString = "",
environment,
@ -247,7 +248,7 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
chartSettings = defaultChartSettings,
}: SquiggleChartProps) => {
let expressionResult = run(squiggleString, bindings, environment, jsImports);
let e = environment ? environment : { sampleCount: 100000, xyPointLength: 1000 };
let e = environment ? environment : defaultEnvironment;
let internal: JSX.Element;
if (expressionResult.tag === "Ok") {
let expression = expressionResult.value;