DKL9 GitList
Repositories
DKL9 home
rtensor
Code
Commits
Branches
Tags
Search
Tree:
6c1aa39
Branches
Tags
master
rtensor
rtensor_lib.js
Restructure further into module-land
dkl9
commited
6c1aa39
at 2023-197 08:48:46
rtensor_lib.js
Blame
History
Raw
"use strict"; import {fts} from "./rtensor_common.js"; import {Complex} from "./types/complex.js"; import {MathsFunc} from "./types/mathsfunc.js"; import {AST, vecOp, NumericType, anyNaN} from "./maths_ast.js"; import {Token} from "./maths_parser.js"; import {RTensorRender} from "./rtensor_to_html.js" AST.prototype.shouldParenMul = RTensorRender.shouldParenMul; AST.prototype.shouldParenExp = RTensorRender.shouldParenExp; AST.prototype.toHTML = RTensorRender.toHTML; export const IDENT_AST = new AST(1, new Token(1, "")); function elemwiseFunc(f) { const ref = (args, de) => (typeof args["0"] == "number" || typeof args["0"] == "bigint" || (typeof args["0"] == "object" && args["0"].map == null)) ? f(args["0"]) : args["0"].map(x => ref({ "0": x }, de)); return new MathsFunc([[[IDENT_AST], ref]], {}); } export const BUILTIN_FUNCS = { // numeric "abs": elemwiseFunc(x => x.mag()), "floor": elemwiseFunc(x => x.floor()), "random": new MathsFunc([[[], function(args, de) { return NumericType.ify(Math.random()); }], [[IDENT_AST, IDENT_AST], function(args, de) { const ub = args["1"]; const lb = args["0"]; return lb.add(NumericType.ify(Math.ceil(ub.sub(lb).add(ub.constructor.one()).mag() * Math.random()))).sub(lb.constructor.one()); }]], {}), // complex "re": elemwiseFunc(z => z.re), "im": elemwiseFunc(z => z.im), "conj": elemwiseFunc(z => new Complex(z.re, -z.im)), // trigonometric "sin": elemwiseFunc(x => x.sin()), "cos": elemwiseFunc(x => x.cos()), "arcsin": elemwiseFunc(x => x.asin()), "arccos": elemwiseFunc(x => x.acos()), "arctan": new MathsFunc([[[IDENT_AST, IDENT_AST], function(args, de) { return vecOp(args["0"], args["1"], (x, y) => y.atan(x)); }], [[IDENT_AST], function(args, de) { return vecOp(args["0"], 0, ((x, _y) => x.atan())); }]], {}), // vector "zero": new MathsFunc([[[IDENT_AST], function(args, de) { const l = args["0"].mag(); if (l >= 0 && Math.floor(l) == l) { return (new Array(l)).fill(NumericType.zero()); } else { throw `cannot make ${fts(l)}-element vector`; } }]], {}), "len": new MathsFunc([[[IDENT_AST], function(args, de) { const a0l = args["0"].length; if (a0l != null) { return NumericType.ify(a0l); } else { throw `${fts(args["0"])} has no length`; } }]], {}), "any": new MathsFunc([[[IDENT_AST], function(args, de) { return args["0"]. map(a => (typeof a == "boolean") ? a : !a.eq(a.constructor.zero())). reduce((a, b) => a || b, 0); }]], {}), "all": new MathsFunc([[[IDENT_AST], function(args, de) { return args["0"]. map(a => (typeof a == "boolean") ? a : !a.eq(a.constructor.zero())). reduce((a, b) => a && b, 1); }]], {}), "map": new MathsFunc([[[IDENT_AST, IDENT_AST], function(args, de) { if (!args["1"].apply) { throw `${fts(args["1"])} is not a function`; } return args["0"].map((x, i) => { if (anyNaN(x)) { return null; } else { // TODO: if the function handles the given argument structure, // but breaks down in evaluation, // it skips to the next argument structure and messes stuff up try { return args["1"].apply(args["1"], [[x, i + 1], de]); } catch (err) { try { return args["1"].apply(args["1"], [[x], de]); } catch (err) { return args["1"].apply(args["1"], [[], de]); } } } }); }]], {}), "filter": new MathsFunc([[[IDENT_AST, IDENT_AST], function(args, de) { return args["0"].filter((x, i) => { if (anyNaN(x)) { return false; } else { let er; try { er = args["1"].apply(args["1"], [[x, i + 1], de]); } catch (err) { er = args["1"].apply(args["1"], [[x], de]); } return (er === true) || ((er !== false) && !er.eq(er.constructor.zero())); } }); }]], {}), "reduce": new MathsFunc([[[IDENT_AST, IDENT_AST], function(args, de) { return args["0"].reduce((x, y) => anyNaN(x) ? null : (anyNaN(y) ? x : args["1"].apply(args["1"], [[x, y], de]))); }]], {}), "tail": new MathsFunc([[[IDENT_AST], function(args, de) { if (args["0"].slice) { return args["0"].slice(1); } else { throw `${args["0"]} has no tail`; } }]], {}), "trim": new MathsFunc([[[IDENT_AST], function(args, de) { if (args["0"].slice) { return args["0"].slice(0, args["0"].length - 1); } else { throw `cannot trim ${args["0"]}`; } }]], {}), "deepmap": new MathsFunc([[[IDENT_AST, IDENT_AST], function(args, de) { return vecOp(args["0"], 0, (x, _y) => args["1"].apply(args["1"], [[x], de])); }]], {}), // non-functions "i": new Complex(0, 1), "pi": Math.PI, "phi": (1 + Math.sqrt(5)) / 2, "true": true, "false": false, "null": { constructor: null }, // other "if": new MathsFunc([[[IDENT_AST, IDENT_AST, IDENT_AST], function(args, de) { if (args["0"].constructor == AST) { const cer = args["0"].evaluate(de); return (typeof cer == "boolean" ? cer : !cer.eq(cer.constructor.zero())) ? args["1"].evaluate(de) : args["2"].evaluate(de); } else { return (args["0"][0] || args["0"]) ? args["1"] : args["2"]; } }], [[IDENT_AST, IDENT_AST, IDENT_AST, IDENT_AST], function(args, de) { const sa = args["0"][0] ? args["1"] : args["2"]; const ar = sa.apply(sa, [[args["3"]], de]); return ar; }], [[IDENT_AST, IDENT_AST, IDENT_AST, IDENT_AST, IDENT_AST], function(args, de) { const sa = args["0"][0] ? args["1"] : args["2"]; const ar = sa.apply(sa, [[args["3"], args["4"]], de]); return ar; }]], {}), "for": new MathsFunc([[[IDENT_AST, IDENT_AST, IDENT_AST], function(args, de) { let state; let ic = 0; const it = Date.now(); for ( state = args["0"]; (x => x[0] == null ? x : x[0])(args["1"].apply(args["1"], [[state], de])); state = args["2"].apply(args["2"], [[state], de]) ) { if (ic > 10000 || ic % 100 == 0 && Date.now() - it > 10000) { throw `suspected infinite for() loop`; } ic++; } return state; }], [[IDENT_AST, IDENT_AST, IDENT_AST, IDENT_AST], function(args, de) { const ide = Object.fromEntries(Object.entries(de)); let ic = 0; const it = Date.now(); for (args["0"].evaluate(ide); args["1"].evaluate(ide); args["2"].evaluate(ide)) { if (ic > 10000 || ic % 100 == 0 && Date.now() - it > 10000) { throw `suspected infinite for() loop`; } ic++; } return args["3"].evaluate(ide); }]], {}), "clear": new MathsFunc([[[], function(args, de) { iol.innerHTML = ""; return 0; }]], {}), "tostring": new MathsFunc([[[IDENT_AST], function(args, de) { const oe = document.createElement("p"); oe.innerText = args["0"].toString(); iol.append(oe); return 0; }]], {}), "render": new MathsFunc([[[IDENT_AST], function(args, de) { const oe = document.createElement("p"); oe.innerHTML = `<span class="maths">${args["0"].toHTML()}</span>`; iol.append(oe); return 0; }]], {}), "html": new MathsFunc([[[IDENT_AST], function(args, de) { const oe = document.createElement("pre"); oe.innerText = `<span class="maths"><table></table>${args["0"].toHTML()}</span>`; iol.append(oe); return 0; }]], {}), "lhs": new MathsFunc([[[IDENT_AST], function(args, de) { const ce = args["0"]; switch (ce.ts) { case 1: case 2: return de.null; case 3: return ce.l; case 4: return ce.l ? ce.m : de.null; case 5: return de.null; case 6: return ce.l; case 7: case 8: return ce.l; default: dl("error: invalid AST node type " + ce.ts); } }]], {}), "rhs": new MathsFunc([[[IDENT_AST], function(args, de) { const ce = args["0"]; switch (ce.ts) { case 1: case 2: return de.null; case 3: return ce.r; case 4: return ce.l ? ce.r : ce.m; case 5: return ce.m; case 6: return ce.m; case 7: case 8: return ce.m; default: dl("error: invalid AST node type " + ts); } }]], {}), "operator": new MathsFunc([[[IDENT_AST], function(args, de) { const ce = args["0"]; switch (ce.ts) { case 1: return -1; case 2: return -2; case 3: return ce.m; case 4: return -4; case 5: return ce.l; case 6: return -6; case 7: case 8: return -7; default: dl("error: invalid AST node type " + ts); } }]], {}), "sum": new MathsFunc([[[IDENT_AST, IDENT_AST, IDENT_AST], function(args, de) { const ide = Object.fromEntries(Object.entries(de)); const ias = args["0"]; // the initialisation statement must be a simple assignment if (!(ias.ts == 3 && ias.m == 11 && ias.l.ts == 1)) { throw `cannot use ${ias} to initialise summation`; } const vn = ias.l.v; const ul = args["1"].evaluate(ide); let ret; let start = true; for (ias.evaluate(ide); ide[vn].lt(ul) || ide[vn].eq(ul); ide[vn] = ide[vn].add(ide[vn].constructor.one())) { if (start) { ret = args["2"].evaluate(ide); start = false; } else { const rhs = args["2"].evaluate(ide); ret = vecOp(ret, rhs, (x, y) => x.add(x.constructor.ify(y))); } } return ret || NumericType.zero(); }]], {}), "product": new MathsFunc([[[IDENT_AST, IDENT_AST, IDENT_AST], function(args, de) { const ide = Object.fromEntries(Object.entries(de)); const ias = args["0"]; // the initialisation statement must be a simple assignment if (!(ias.ts == 3 && ias.m == 11 && ias.l.ts == 1)) { throw `cannot use ${ias} to initialise product`; } const vn = ias.l.v; const ul = args["1"].evaluate(ide); let ret; let start = true; for (ias.evaluate(ide); ide[vn] <= ul; ide[vn]++) { if (start) { ret = args["2"].evaluate(ide); start = false; } else { ret = vecOp(ret, args["2"].evaluate(ide), (x, y) => x.mul(y)); } } return ret || NumericType.one(); }]], {}), }; function dl(v) { // required by maths_parser, but doesn't actually have to do anything ioLog(v); return null; } try { window.onerror = (m, s, l, c, e) => dl(`${s}:${l}:${c}: ${m}`); } catch (_e) {}