Skip to content

Instantly share code, notes, and snippets.

@jared-hughes
Created January 4, 2022 00:22
Show Gist options
  • Save jared-hughes/b89c70810eca81cbed37071583b1228f to your computer and use it in GitHub Desktop.
Save jared-hughes/b89c70810eca81cbed37071583b1228f to your computer and use it in GitHub Desktop.
Desmos Dependency Analysis
function computeContext() {
// Emulate what happens in the web worker
const Context = require("core/math/context").Context;
const context = new Context();
const changeSet = {
isCompleteState: true,
statements: {},
};
for (let stmt of Calc.controller.getAllItemModels()) {
if (stmt.type !== "expression") continue;
changeSet.statements[stmt.id] = stmt;
}
const ticker = Calc.controller.listModel.ticker.cachedParsableState;
if (ticker.handlerLatex) {
changeSet.statements[ticker] = ticker;
}
context.processChangeSet(changeSet);
context.updateAnalysis();
return context;
}
function isSubset(a, b) {
return [...a].every((e) => b.has(e));
}
function flatMapUnique(list, fn) {
return [...new Set(list.flatMap(fn))];
}
function getAllTrees(rawTree) {
if (!rawTree) return [];
return [
rawTree,
rawTree.metaData?.colorLatex,
rawTree.metaData?.clickHandler,
].filter((e) => e);
}
{
const ctx = computeContext();
const dependentCount = {};
const dependentIDs = {};
const updaterCount = {};
function getUpdates(data) {
return data._updateSymbols.filter((e) => !ctx.parent_frame[e]);
}
function getDependencies(data) {
return data._dependencies
.filter((e) => !data._dummyDependencies.includes(e))
.filter((e) => !ctx.parent_frame[e]);
}
for (let analysis of Object.values(ctx.analysis)) {
flatMapUnique(getAllTrees(analysis.rawTree), getUpdates).forEach((symb) => {
updaterCount[symb] = (updaterCount[symb] ?? 0) + 1;
});
flatMapUnique(getAllTrees(analysis.rawTree), getDependencies).forEach(
(symb) => {
dependentCount[symb] = (dependentCount[symb] ?? 0) + 1;
dependentIDs[symb] ??= [];
dependentIDs[symb].push(analysis.rawTree.userData.id);
}
);
}
const stateDependencies = {};
const stateDependenciesByID = {};
function getStateDependencies(symb) {
if (updaterCount[symb]) {
return [symb];
}
if (ctx.parent_frame[symb]) return [];
if (stateDependencies[symb]) return stateDependencies[symb];
const sd = (stateDependencies[symb] = new Set());
for (let dep of flatMapUnique(
getAllTrees(ctx.frame[symb]),
getDependencies
)) {
getStateDependencies(dep).forEach((stateDep) => {
sd.add(stateDep);
});
}
return sd;
}
for (let analysis of Object.values(ctx.analysis)) {
const sd = (stateDependenciesByID[analysis.rawTree.userData.id] =
new Set());
flatMapUnique(getAllTrees(analysis.rawTree), getDependencies).forEach(
(symb) => {
getStateDependencies(symb).forEach((stateDep) => {
sd.add(stateDep);
});
}
);
}
console.log("State variables:", Object.keys(updaterCount));
console.log(
"Variables with exactly one dependent:",
Object.keys(dependentCount).filter((symb) => dependentCount[symb] === 1)
);
console.log(
"Variables with zero dependents:",
Object.keys(ctx.frame)
.filter((symb) => !symb.startsWith("ans_"))
.filter((symb) => !dependentCount[symb])
);
console.log(
"Variables with exactly one dependent, who does not depend on any more state vars (may be possible/worth inlining):",
Object.keys(dependentCount)
.filter((symb) => dependentCount[symb] === 1)
.filter((symb) =>
isSubset(
stateDependenciesByID[dependentIDs[symb][0]] ?? new Set(),
stateDependencies[symb] ?? new Set()
)
)
);
}
// Warning: doesn't include image dependencies (center etc.)
// Also, dynamic-scoped variables (wack-scope variables) change some of the output
// There's a bunch of other minor issues, so don't accept the output blindly, but it's a good first approximation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment