@casadi/casadi-wasm
Version:
CasADi — symbolic framework for algorithmic differentiation and numerical optimization, compiled to WebAssembly. Runs in Node.js with on-demand solver plugins (ipopt, fatrop, sundials, ...).
104 lines (93 loc) • 4.46 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8">
<title>CasADi in the browser — @casadi/casadi-wasm via unpkg</title>
<style>
body { font: 14px/1.6 ui-monospace, Menlo, Consolas, monospace; margin: 2rem; max-width: 60rem; color: #222; }
h1 { font-size: 1.2rem; } a { color: #1565c0; }
#out { background: #f5f5f5; border: 1px solid #ddd; padding: 1rem; white-space: pre-wrap; min-height: 6rem; }
button { font: inherit; padding: .35rem .9rem; margin-bottom: .6rem; }
.err { color: #c62828; }
small { color: #666; }
</style>
</head>
<body>
<h1>CasADi running in your browser 🐫</h1>
<p><small>Loaded straight from
<a href="https://unpkg.com/@casadi/casadi-wasm@3.8.0-beta.1/">unpkg</a>
— no install, no server-side compute. Symbolic AD + an IPOPT solve, all WebAssembly.</small></p>
<button id="run" disabled>Run again</button>
<div id="out">Loading casadi.wasm from unpkg…</div>
<script>;
// Pin the published prerelease. Use "@beta" to always track the latest beta.
const BASE = "https://unpkg.com/@casadi/casadi-wasm@3.8.0-beta.1/";
// --- Self-contained browser loader -------------------------------------
// casadi.js is generated as CommonJS for Node (require('./casadi_wasm.js'),
// require('path'), __dirname). We fetch it from unpkg and evaluate it in a
// tiny CJS sandbox that supplies exactly those, so the unmodified file runs
// in the browser. (Mirrors the package's own examples/_casadi_browser.js.)
async function loadCasadi(base) {
const evalCjs = async (file, requireFn) => {
const resp = await fetch(base + file);
if (!resp.ok) throw new Error(`${base + file}: ${resp.status} ${resp.statusText}`);
const src = await resp.text();
const factory = new Function("module", "exports", "require", "__dirname", "__filename",
src + "\n;return module.exports;");
const m = { exports: {} };
return factory(m, m.exports, requireFn, base.replace(/\/$/, ""), base + file);
};
const createWasm = await evalCjs("casadi_wasm.js",
(p) => { throw new Error("casadi_wasm.js: unexpected require " + p); });
const createcasadi = await evalCjs("casadi.js", (p) => {
if (p.endsWith("casadi_wasm.js")) return createWasm;
if (p === "path") return { join: (...a) => a.filter(Boolean).join("/").replace(/\/{2,}/g, "/") };
throw new Error("casadi.js: unexpected require " + p);
});
return await createcasadi();
}
// Plugin .so's are fetched lazily by name; redirect those bare requests to
// unpkg so on-demand solver loading works cross-origin from a static page.
const _fetch = window.fetch.bind(window);
window.fetch = (u, o) =>
_fetch(typeof u === "string" && /^libcasadi_[\w]+\.so$/.test(u) ? BASE + u : u, o);
// --- The demo ----------------------------------------------------------
async function demo(ca, log) {
// JavaScript has no operator overloading: use ca.plus/minus/times, etc.
const x = ca.SX.sym("x", 2);
const [x0, x1] = ca.vertsplit(x);
const f = ca.plus(ca.times(x0, x0), ca.times(x1, x1)); // f = x0^2 + x1^2
log("symbolic f = " + String(f));
// Algorithmic differentiation: exact Jacobian, evaluated numerically.
const F = ca.Function("F", [x], [f]);
const J = ca.Function("J", [x], [ca.jacobian(f, x)]);
const xv = ca.DM([1.5, -2.0]);
log("f([1.5, -2]) = " + String(F(xv)));
log("∂f/∂x at that x = " + String(J(xv)));
// Constrained optimization with IPOPT (loaded on demand from unpkg).
log("\nloading ipopt…");
await ca.load_nlpsol("ipopt");
const nlp = { x: x, f: f, g: ca.minus(ca.plus(x0, x1), ca.SX(10)) }; // x0 + x1 - 10
const solver = ca.nlpsol("solver", "ipopt", nlp);
const sol = solver.call({ lbg: ca.DM(0) }); // subject to g >= 0
log("argmin x0^2+x1^2 s.t. x0+x1 >= 10");
log(" x* = " + String(sol["x"]));
log(" f* = " + String(sol["f"]));
}
// --- Wire up the page --------------------------------------------------
const out = document.getElementById("out");
const btn = document.getElementById("run");
const log = (...a) => { out.textContent += a.join(" ") + "\n"; };
const run = (ca) => { out.textContent = ""; return demo(ca, log); };
loadCasadi(BASE).then((ca) => {
btn.disabled = false;
btn.onclick = () => run(ca).catch((e) => log("FATAL: " + (e.message || e)));
return run(ca);
}).catch((e) => {
out.textContent = "Failed to load casadi: " + (e.message || e);
out.className = "err";
});
</script>
</body>
</html>