Created
January 4, 2022 00:22
-
-
Save jared-hughes/b89c70810eca81cbed37071583b1228f to your computer and use it in GitHub Desktop.
Desmos Dependency Analysis
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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