Skip to content

Commit

Permalink
Merge pull request #239 from flatironinstitute/analysis-data.json
Browse files Browse the repository at this point in the history
Allow analysis scripts to read data.json
  • Loading branch information
WardBrian authored Nov 4, 2024
2 parents 87fb927 + 4733e23 commit 7b7e63d
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 25 deletions.
16 changes: 11 additions & 5 deletions gui/src/app/Scripting/Analysis/AnalysisPyWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const AnalysisPyWindow: FunctionComponent<AnalysisWindowProps> = ({
onStatus,
runnable,
notRunnableReason,
files,
} = useAnalysisState(latestRun);

const callbacks = useMemo(
Expand All @@ -51,13 +52,18 @@ const AnalysisPyWindow: FunctionComponent<AnalysisWindowProps> = ({
const handleRun = useCallback(
(code: string) => {
clearOutputDivs(consoleRef, imagesRef);
run(code, spData, {
loadsDraws: true,
showsPlots: true,
producesData: false,
run({
code,
spData,
spRunSettings: {
loadsDraws: true,
showsPlots: true,
producesData: false,
},
files,
});
},
[consoleRef, imagesRef, run, spData],
[consoleRef, imagesRef, run, spData, files],
);

const contentOnEmpty = useTemplatedFillerText(
Expand Down
6 changes: 4 additions & 2 deletions gui/src/app/Scripting/Analysis/AnalysisRWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ const AnalysisRWindow: FunctionComponent<AnalysisWindowProps> = ({
onStatus,
runnable,
notRunnableReason,
files,
} = useAnalysisState(latestRun);

const { run } = useWebR({ consoleRef, imagesRef, onStatus });

const handleRun = useCallback(
async (userCode: string) => {
clearOutputDivs(consoleRef, imagesRef);
const code = loadDrawsCode + userCode;
await run({ code, spData });
await run({ code, spData, files });
},
[consoleRef, imagesRef, run, spData],
[consoleRef, imagesRef, run, spData, files],
);

const contentOnEmpty = useTemplatedFillerText(
Expand Down
4 changes: 4 additions & 0 deletions gui/src/app/Scripting/Analysis/analysis_template.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ install.packages("bayesplot")
library(bayesplot)

mcmc_hist(draws, pars = c("lp__"))

# can also access data.json
data <- jsonlite::read_json("./data.json")
data
7 changes: 7 additions & 0 deletions gui/src/app/Scripting/Analysis/analysis_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,10 @@
plt.hist(samples.ravel(), bins=30)
plt.title("lp__")
plt.show()

# you can also read data.json
import json

with open("data.json") as f:
data = json.load(f)
print(data)
12 changes: 12 additions & 0 deletions gui/src/app/Scripting/Analysis/useAnalysisState.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FileNames } from "@SpCore/FileMapping";
import {
InterpreterStatus,
isInterpreterBusy,
Expand Down Expand Up @@ -44,6 +45,16 @@ const useAnalysisState = (latestRun: StanRun) => {

const isDataDefined = useMemo(() => spData !== undefined, [spData]);

const files = useMemo(() => {
if (latestRun.data === undefined) {
return undefined;
} else {
return {
[FileNames.DATAFILE]: latestRun.data,
};
}
}, [latestRun.data]);

useEffect(() => {
if (!isDataDefined) {
setRunnable(false);
Expand All @@ -65,6 +76,7 @@ const useAnalysisState = (latestRun: StanRun) => {
onStatus: setStatus,
runnable,
notRunnableReason,
files,
};
};

Expand Down
7 changes: 3 additions & 4 deletions gui/src/app/Scripting/DataGeneration/DataPyWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,14 @@ const DataPyWindow: FunctionComponent<Props> = () => {
const handleRun = useCallback(
(code: string) => {
clearOutputDivs(consoleRef);
run(
run({
code,
{},
{
spRunSettings: {
loadsDraws: false,
showsPlots: false,
producesData: true,
},
);
});
},
[consoleRef, run],
);
Expand Down
16 changes: 15 additions & 1 deletion gui/src/app/Scripting/pyodide/pyodideWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,14 @@ self.onmessage = async (e) => {
return;
}
const message = e.data;
await run(message.code, message.spData, message.spRunSettings);
await run(message.code, message.spData, message.spRunSettings, message.files);
};

const run = async (
code: string,
spData: Record<string, any> | undefined,
spPySettings: PyodideRunSettings,
files: Record<string, string> | undefined,
) => {
setStatus("loading");
try {
Expand Down Expand Up @@ -113,12 +114,25 @@ const run = async (
await f;
}

if (files) {
const encoder = new TextEncoder();
for (const [filename, content] of Object.entries(files)) {
await pyodide.FS.writeFile(filename, encoder.encode(content + "\n"));
}
}

setStatus("running");
pyodide.runPython(script, { globals });
succeeded = true;
} catch (e: any) {
console.error(e);
sendStderr(e.toString());
} finally {
if (files) {
for (const filename of Object.keys(files)) {
await pyodide.FS.unlink(filename);
}
}
}

if (spPySettings.producesData) {
Expand Down
1 change: 1 addition & 0 deletions gui/src/app/Scripting/pyodide/pyodideWorkerTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type MessageToPyodideWorker = {
code: string;
spData: Record<string, any> | undefined;
spRunSettings: PyodideRunSettings;
files: Record<string, string> | undefined;
};

export const isMessageToPyodideWorker = (
Expand Down
22 changes: 11 additions & 11 deletions gui/src/app/Scripting/pyodide/usePyodideWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ type PyodideWorkerCallbacks = {
onImage?: (image: string) => void;
};

type RunPyProps = {
code: string;
spData?: Record<string, any>;
spRunSettings: PyodideRunSettings;
files?: Record<string, string>;
};

class PyodideWorkerInterface {
#worker: Worker | undefined;

Expand Down Expand Up @@ -71,16 +78,13 @@ class PyodideWorkerInterface {
return { worker, cleanup };
}

run(
code: string,
spData: Record<string, any> | undefined,
spRunSettings: PyodideRunSettings,
) {
run({ code, spData, spRunSettings, files }: RunPyProps) {
const msg: MessageToPyodideWorker = {
type: "run",
code,
spData,
spRunSettings,
files,
};
if (this.#worker) {
this.#worker.postMessage(msg);
Expand Down Expand Up @@ -114,13 +118,9 @@ const usePyodideWorker = (callbacks: {
}, [callbacks]);

const run = useCallback(
(
code: string,
spData: Record<string, any> | undefined,
spRunSettings: PyodideRunSettings,
) => {
(p: RunPyProps) => {
if (worker) {
worker.run(code, spData, spRunSettings);
worker.run(p);
} else {
throw new Error("pyodide worker is not defined");
}
Expand Down
14 changes: 13 additions & 1 deletion gui/src/app/Scripting/webR/useWebR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type useWebRProps = {
type RunRProps = {
code: string;
spData?: Record<string, any>;
files?: Record<string, string>;
};

const useWebR = ({ imagesRef, consoleRef, onStatus, onData }: useWebRProps) => {
Expand Down Expand Up @@ -63,7 +64,7 @@ const useWebR = ({ imagesRef, consoleRef, onStatus, onData }: useWebRProps) => {
}, [consoleRef, imagesRef, onStatus, webR]);

const run = useCallback(
async ({ code, spData }: RunRProps) => {
async ({ code, spData, files }: RunRProps) => {
try {
const webR = await loadWebRInstance();
const shelter = await new webR.Shelter();
Expand All @@ -86,6 +87,12 @@ const useWebR = ({ imagesRef, consoleRef, onStatus, onData }: useWebRProps) => {

const options = { ...captureOutputOptions, env };

if (files) {
const encoder = new TextEncoder();
for (const [name, content] of Object.entries(files)) {
await webR.FS.writeFile(name, encoder.encode(content + "\n"));
}
}
// setup
await webR.evalRVoid(webRPreamble, options);
await webR.evalRVoid(code, options);
Expand All @@ -98,6 +105,11 @@ const useWebR = ({ imagesRef, consoleRef, onStatus, onData }: useWebRProps) => {
}
} finally {
shelter.purge();
if (files) {
for (const [name] of Object.entries(files)) {
await webR.FS.unlink(name);
}
}
}
onStatus("completed");
} catch (e: any) {
Expand Down
2 changes: 1 addition & 1 deletion gui/src/app/StanSampler/StanSampler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class StanSampler {
};
if (!this.#stanWorker) throw new Error("model worker is undefined");

this.update({ type: "startSampling", samplingOpts });
this.update({ type: "startSampling", samplingOpts, data });

this.#samplingStartTimeSec = Date.now() / 1000;
this.postMessage({ purpose: Requests.Sample, sampleConfig });
Expand Down
3 changes: 3 additions & 0 deletions gui/src/app/StanSampler/useStanSampler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type StanRun = {
errorMessage: string;
progress?: Progress;
samplingOpts?: SamplingOpts;
data?: string;
draws?: number[][];
paramNames?: string[];
computeTimeSec?: number;
Expand All @@ -35,6 +36,7 @@ export type StanRunAction =
| {
type: "startSampling";
samplingOpts: SamplingOpts;
data: string;
}
| {
type: "samplerReturn";
Expand Down Expand Up @@ -65,6 +67,7 @@ export const StanRunReducer = (
status: "sampling",
errorMessage: "",
samplingOpts: action.samplingOpts,
data: action.data,
};
case "samplerReturn":
return {
Expand Down

0 comments on commit 7b7e63d

Please sign in to comment.