Skip to content

Commit 9f0214c

Browse files
committed
adding sympy solver
1 parent 8ae560a commit 9f0214c

File tree

12 files changed

+429
-173
lines changed

12 files changed

+429
-173
lines changed

package-lock.json

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"echarts-for-react": "^3.0.2",
2424
"mathml-to-latex": "^1.5.0",
2525
"pako": "^2.1.0",
26+
"pyodide": "^0.28.0",
2627
"react": "^19.1.0",
2728
"react-dom": "^19.1.0",
2829
"visiojs": "^0.0.0"

src/App.jsx

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import MyChart from "./PlotTF.jsx";
2222
import Container from "@mui/material/Container";
2323
import Snackbar from "@mui/material/Snackbar";
2424
import SnackbarContent from "@mui/material/SnackbarContent";
25+
import Button from "@mui/material/Button";
26+
import Grid from "@mui/material/Grid";
2527

2628
const initialComponents = {
2729
L0: {
@@ -103,10 +105,14 @@ function new_calculate_tf(textResult, fRange, numSteps, components) {
103105

104106
//Now only remaining variable is S, substitute that and solve. Also swap power ^ for **
105107
const re = /s/gi;
108+
const reDollar = /\$/gi;
106109
const re2 = /\^/gi;
107-
const re3 = /abs/gi; //sometimes abs(C0) is left in the equation
108-
var res = complex_freq.replace(re2, "**");
109-
res = res.replace(re3, "");
110+
var res = complex_freq.replace(re2, "**"); //swap ^ for **
111+
// const re3 = /abs/gi; //sometimes abs(C0) is left in the equation
112+
// res = res.replace(re3, "");
113+
//now swap sqrt for '$'
114+
const re3 = /sqrt/gi; //sometimes abs(C0) is left in the equation
115+
res = res.replace(re3, "$"); //swap sqrt for $
110116

111117
var fstepdB_20 = Math.log10(fRange.fmax / fRange.fmin) / numSteps;
112118
var fstep = 10 ** fstepdB_20;
@@ -118,7 +124,8 @@ function new_calculate_tf(textResult, fRange, numSteps, components) {
118124
freq.push(f);
119125
// const mathString = res.replace(re, 2 * Math.PI * f).replace(/\*\*/g, "^");
120126
// evalNew = evaluate(mathString);
121-
const mathString = res.replace(re, 2 * Math.PI * f);
127+
const mathString = res.replace(re, 2 * Math.PI * f).replace(reDollar, "Math.sqrt");
128+
// console.log("evalNew", mathString);
122129
evalNew = eval(mathString);
123130

124131
absNew = Math.abs(evalNew);
@@ -221,14 +228,14 @@ function App() {
221228
return url.toString();
222229
}
223230

224-
function handleRequestBilin() {
231+
async function handleRequestBilin() {
225232
// console.log("handleRequestBilin", calcBilinear());
226-
const [raw, bilin] = calcBilinear();
233+
const [raw, bilin] = await calcBilinear(results.solver);
227234
// setBilinearMathML(`<math>${bilin}</math>`);
228235
// setBilinearRaw(raw);
229236
setResults({ ...results, bilinearRaw: raw, bilinearMathML: `<math>${bilin}</math>` });
230237
}
231-
// console.log("render");
238+
// console.log(results);
232239

233240
// Update the DOM
234241
return (
@@ -262,30 +269,37 @@ function App() {
262269
</div>
263270
</div>
264271
<div className="row shadow-sm rounded bg-lightgreen my-2 py-3" id="schematic">
265-
<ChoseTF
266-
setResults={setResults}
267-
nodes={nodes}
268-
fullyConnectedComponents={fullyConnectedComponents}
269-
componentValuesSolved={componentValuesSolved}
270-
/>
272+
<ChoseTF setResults={setResults} nodes={nodes} fullyConnectedComponents={fullyConnectedComponents} componentValuesSolved={componentValuesSolved} />
271273
{results.text != "" && (
272274
<>
273-
<DisplayMathML title="Laplace" textResult={results.text} mathML={results.mathML} caclDone={results.text != ""} />
275+
<DisplayMathML title="Laplace Transform" textResult={results.text} mathML={results.mathML} caclDone={results.text != ""} />
276+
{results.numericText != null && (
277+
<DisplayMathML title="Laplace Transform (numeric)" textResult={results.numericText} mathML={results.numericML} caclDone={results.text != ""} />
278+
)}
274279
<div className="col-12">
275280
<MyChart freq_new={freq_new} mag_new={mag_new} />
276281
</div>
277282
<FreqAdjusters settings={settings} setSettings={setSettings} />
278-
<DisplayMathML
279-
title="Bilinear"
280-
textResult={results.bilinearRaw}
281-
mathML={results.bilinearMathML}
282-
handleRequestBilin={() => handleRequestBilin()}
283-
caclDone={results.text != ""}
284-
/>
283+
{results.bilinearMathML == "" ? (
284+
<Grid container spacing={1}>
285+
<Button
286+
variant="contained"
287+
color="info"
288+
sx={{ m: 3 }}
289+
onClick={async () => {
290+
handleRequestBilin();
291+
}}
292+
>
293+
Calculate bilinear transform
294+
</Button>
295+
</Grid>
296+
) : (
297+
<DisplayMathML title="Bilinear Transform" textResult={results.bilinearRaw} mathML={results.bilinearMathML} caclDone={results.text != ""} />
298+
)}
285299
</>
286300
)}
287301
</div>
288-
<div key="releaseNotes" className="row my-2 py-1 shadow-sm rounded bg-lightgreen">
302+
<div key="releaseNotes" className="row my-2 py-1">
289303
<ReleaseNotes />
290304
</div>
291305
<div key="comments" className="row my-2 py-1 shadow-sm rounded bg-lightgreen">

src/ChoseTF.jsx

Lines changed: 99 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,36 @@
11
import Button from "@mui/material/Button";
22
import Stack from "@mui/material/Stack";
3-
// import ToggleButton from "@mui/material/ToggleButton";
4-
// import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
3+
import ToggleButton from "@mui/material/ToggleButton";
4+
import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
55
import Box from "@mui/material/Box";
66
import Grid from "@mui/material/Grid";
7-
// import { useState } from "react";
7+
import { useState } from "react";
88
import { build_and_solve_mna } from "./new_solveMNA.js";
9+
import { styled } from "@mui/material/styles";
10+
import Tooltip, { tooltipClasses } from "@mui/material/Tooltip";
11+
import CircularProgress from "@mui/material/CircularProgress";
12+
import { initPyodideAndSympy } from "./pyodideLoader";
13+
import { emptyResults } from "./common.js"; // Import the emptyResults object
14+
15+
const HtmlTooltip = styled(({ className, ...props }) => <Tooltip {...props} classes={{ popper: className }} />)(({ theme }) => ({
16+
[`& .${tooltipClasses.tooltip}`]: {
17+
backgroundColor: "#f5f5f9",
18+
color: "rgba(0, 0, 0, 0.87)",
19+
maxWidth: 220,
20+
fontSize: theme.typography.pxToRem(12),
21+
border: "1px solid #dadde9",
22+
},
23+
}));
24+
25+
function formatMathML(mathml, p, drivers) {
26+
return `<math><mfrac><mrow><mi>${p}</mi></mrow><mrow><msub><mi>${drivers[0] == "vin" ? "V" : "I"}</mi><mi>in</mi></msub></mrow></mfrac><mo>=</mo>${mathml}</math>`;
27+
}
928

1029
export function ChoseTF({ setResults, nodes, fullyConnectedComponents, componentValuesSolved }) {
11-
// const [algebraic, setAlgebraic] = useState("algebraic");
12-
const algebraic = "algebraic";
30+
const [algebraic, setAlgebraic] = useState("algebrite");
31+
const [loading, setLoading] = useState(false);
32+
const [loadedPyo, setLoadedPyo] = useState(null);
33+
// const algebraic = "algebraic";
1334
const probes = [];
1435
const drivers = [];
1536
for (const c in fullyConnectedComponents) if (["vin", "iin"].includes(fullyConnectedComponents[c].type)) drivers.push(fullyConnectedComponents[c].type);
@@ -25,8 +46,8 @@ export function ChoseTF({ setResults, nodes, fullyConnectedComponents, component
2546
// add a value field based on if user chose algebraic or numeric
2647
const valueForAlgebra = {};
2748
for (const c in fullyConnectedComponents) {
28-
if (algebraic == "numeric" && c in componentValuesSolved) valueForAlgebra[c] = componentValuesSolved[c];
29-
else valueForAlgebra[c] = c;
49+
if (c in componentValuesSolved) valueForAlgebra[c] = componentValuesSolved[c];
50+
// else valueForAlgebra[c] = c;
3051
}
3152
// console.log("componentValuesSolved", componentValuesSolved, fullyConnectedComponents, algebraic);
3253
return (
@@ -49,15 +70,34 @@ export function ChoseTF({ setResults, nodes, fullyConnectedComponents, component
4970
<Button
5071
key={p}
5172
variant="contained"
73+
loading={loading}
5274
color="info"
5375
fullWidth
5476
sx={{ py: 1, justifyContent: "center", fontSize: "1.4em" }}
55-
onClick={() => {
56-
const [textResult, mathml, complex_response] = build_and_solve_mna(nodes.length, int_probes, fullyConnectedComponents, valueForAlgebra);
57-
const editedMathMl = `<math><mfrac><mrow><mi>${p}</mi></mrow><mrow><msub><mi>${
58-
drivers[0] == "vin" ? "V" : "I"
59-
}</mi><mi>in</mi></msub></mrow></mfrac><mo>=</mo>${mathml}</math>`;
60-
setResults({ text: textResult, mathML: editedMathMl, complexResponse: complex_response, bilinearRaw: "", bilinearMathML: "" });
77+
onClick={async () => {
78+
setLoading(true);
79+
setResults({ ...emptyResults }); // Reset results to empty
80+
const [textResult, mathml, complex_response, numericResult, numericText] = await build_and_solve_mna(
81+
nodes.length,
82+
int_probes,
83+
fullyConnectedComponents,
84+
valueForAlgebra,
85+
loadedPyo,
86+
algebraic,
87+
);
88+
const editedMathMl = formatMathML(mathml, p, drivers);
89+
const editedMathMlNumeric = formatMathML(numericResult, p, drivers);
90+
setResults({
91+
text: textResult,
92+
mathML: editedMathMl,
93+
complexResponse: complex_response,
94+
bilinearRaw: "",
95+
bilinearMathML: "",
96+
numericML: editedMathMlNumeric,
97+
numericText: numericText,
98+
solver: loadedPyo,
99+
});
100+
setLoading(false);
61101
// setTextResult(textResult);
62102
// setMathML(editedMathMl);
63103
// setComplexResponse(complex_response);
@@ -86,12 +126,53 @@ export function ChoseTF({ setResults, nodes, fullyConnectedComponents, component
86126
</>
87127
)}
88128
{/* Enable this feature if this ticket gets solved! https://github.com/Yaffle/Expression/issues/15 */}
89-
{/* <Box display="flex" gap={1}>
90-
<ToggleButtonGroup color="primary" value={algebraic} exclusive onChange={() => setAlgebraic(algebraic == "algebraic" ? "numeric" : "algebraic")}>
91-
<ToggleButton value="algebraic">Algebraic</ToggleButton>
92-
<ToggleButton value="numeric">Numeric</ToggleButton>
129+
<Box display="flex" gap={1}>
130+
<ToggleButtonGroup color="primary" value={algebraic} exclusive>
131+
<HtmlTooltip
132+
title={
133+
<>
134+
<h6>Chose Algebra solver</h6>
135+
{"These are JavaScript based solvers. It's already loaded and is fine for most cases"}
136+
</>
137+
}
138+
>
139+
<ToggleButton
140+
value="algebrite"
141+
onClick={() => {
142+
setResults({ ...emptyResults });
143+
setLoadedPyo(null);
144+
setAlgebraic("algebrite");
145+
}}
146+
>
147+
{loading ? <CircularProgress size={20} color="inherit" /> : "Algebrite + Yaffle"}
148+
</ToggleButton>
149+
</HtmlTooltip>
150+
<HtmlTooltip
151+
title={
152+
<>
153+
<h6>Chose Algebra solver</h6>
154+
{
155+
"This is a Python based solver. Chosing this will download 10MB of files and run Python inside the browser, enabling more features such as giving a pretty numeric result, and more advanced algebraic simplification."
156+
}
157+
</>
158+
}
159+
>
160+
<ToggleButton
161+
value="sympy"
162+
onClick={async () => {
163+
setLoading(true);
164+
setResults({ ...emptyResults });
165+
const pyodide = await initPyodideAndSympy();
166+
setLoadedPyo(pyodide);
167+
setLoading(false);
168+
setAlgebraic("sympy");
169+
}}
170+
>
171+
{loading ? <CircularProgress size={20} color="inherit" /> : "SymPy"}
172+
</ToggleButton>
173+
</HtmlTooltip>
93174
</ToggleButtonGroup>
94-
</Box> */}
175+
</Box>
95176
</Grid>
96177
);
97178
}

src/DisplayMathML.jsx

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,10 @@ function DangerousSetIn({ mathMLString }) {
99
return <div style={{ textAlign: "center" }} dangerouslySetInnerHTML={{ __html: mathMLString }} />;
1010
}
1111
/*{<DangerousSetIn mathMLString={mathML} />}*/
12-
export function DisplayMathML({ title, mathML, handleRequestBilin, textResult, caclDone }) {
12+
export function DisplayMathML({ title, mathML, textResult, caclDone }) {
1313
const [latexToast, setLatexToast] = useState(false);
1414
const [mathMLToast, setMathMLToast] = useState(false);
1515
if (!caclDone) return null;
16-
// if (tfType == "Laplace") return null;
17-
// else if (tfType == "Bilinear") {
18-
// return (
19-
// <div className="col">
20-
// <button
21-
// type="button"
22-
// className="btn btn-outline-primary py-0"
23-
// onClick={() => {
24-
// handleRequestBilin();
25-
// }}
26-
// >
27-
// Calculate bilinear transform
28-
// </button>
29-
// </div>
30-
// );
31-
// }
32-
// }
3316
return (
3417
<>
3518
<Snackbar open={latexToast} autoHideDuration={10000} onClose={() => setLatexToast(false)} anchorOrigin={{ vertical: "top", horizontal: "right" }}>
@@ -61,22 +44,10 @@ export function DisplayMathML({ title, mathML, handleRequestBilin, textResult, c
6144
<Grid container spacing={1}>
6245
<Grid size={{ xs: 12, sm: 10 }}>
6346
<Grid size={12} sx={{ my: 2 }}>
64-
<h3>{title} Transform</h3>
47+
<h3>{title}</h3>
6548
</Grid>
66-
<Grid size={12} style={{ overflow: "auto" }} sx={{ fontSize: "1.6em" }}>
67-
{mathML == "" && title == "Bilinear" ? (
68-
<Button
69-
variant="contained"
70-
color="info"
71-
onClick={() => {
72-
handleRequestBilin();
73-
}}
74-
>
75-
Calculate bilinear transform
76-
</Button>
77-
) : (
78-
<DangerousSetIn mathMLString={mathML} />
79-
)}
49+
<Grid size={12} style={{ overflow: "auto" }} sx={{ fontSize: "1.6em", minHeight: "55px" }}>
50+
<DangerousSetIn mathMLString={mathML} />
8051
</Grid>
8152
</Grid>
8253
<Grid size={{ xs: 12, sm: 2 }}>

src/ReleaseNotes.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ function ReleaseNotes() {
5454
<li>It could crash, like when you drag one componenct directly on top of another</li>
5555
<li>Limited features compared to industial tools</li>
5656
</ul>
57-
Now it works on touch because I developed visiojs (https://www.npmjs.com/package/visiojs). Also visiojs replaces draw2d which was the source of all the crashing.
58-
This site isn't feature rich - it's original purpose was to create Laplace transforms. But it does run MNA in the background so many more features could be added
57+
Now it works on touch because I developed visiojs (https://www.npmjs.com/package/visiojs). Also visiojs replaces draw2d which was the source of all the
58+
crashing. This site isn't feature rich - it's original purpose was to create Laplace transforms. But it does run MNA in the background so many more features
59+
could be added
5960
</li>
6061
<li>Moved to React + MUI + NPM. This allows: running lint, more maintainable code, smaller file size, many micro-benefits from joining the mainstream</li>
6162
<li>As well as a re-write, the following new features are added:</li>

0 commit comments

Comments
 (0)