calculate dependencies

pull/118/head
Alexander Bentkamp 3 years ago
parent df8fa2933b
commit 0d24232685

@ -628,6 +628,71 @@ def getDocstring (env : Environment) (name : Name) (type : InventoryType) :
-- TODO: for definitions not implemented yet, does it work? -- TODO: for definitions not implemented yet, does it work?
| .Definition => findDocString? env name | .Definition => findDocString? env name
partial def removeTransitiveAux (id : Name) (arrows : HashMap Name (HashSet Name))
(newArrows : HashMap Name (HashSet Name)) (decendants : HashMap Name (HashSet Name)) :
HashMap Name (HashSet Name) × HashMap Name (HashSet Name) := Id.run do
match (newArrows.find? id, decendants.find? id) with
| (some _, some _) => return (newArrows, decendants)
| _ =>
let mut newArr := newArrows
let mut desc := decendants
desc := desc.insert id {} -- mark as worked in case of loops
newArr := newArr.insert id {} -- mark as worked in case of loops
let children := arrows.find! id
let mut trimmedChildren := children
let mut theseDescs := children
for child in children do
(newArr, desc) := removeTransitiveAux child arrows newArr desc
let childDescs := desc.find! child
theseDescs := theseDescs.insertMany childDescs
for d in childDescs do
trimmedChildren := trimmedChildren.erase d
desc := desc.insert id theseDescs
newArr := newArr.insert id trimmedChildren
return (newArr, desc)
def removeTransitive (arrows : HashMap Name (HashSet Name)) : CommandElabM (HashMap Name (HashSet Name)) := do
let mut newArr := {}
let mut desc := {}
for id in arrows.toArray.map Prod.fst do
(newArr, desc) := removeTransitiveAux id arrows newArr desc
if (desc.find! id).contains id then logWarning m!"Loop at {id}"
return newArr
/-- Check if graph contains loops -/
partial def findLoops (arrows : HashMap Name (HashSet Name)) (visited0 : HashSet Name := {}): List Name := Id.run do
let mut visited : HashSet Name := visited0
let all : Array Name := arrows.toArray.map (·.1)
-- find some node that we haven't visited
let some x := all.find? fun x => ¬ visited.contains x
| return [] -- We have visted all nodes and found no loops
visited := visited.insert x
match visitSuccessors x x visited with -- visit all recursive successors of x
| .inl visited' => visited := visited'
| .inr l => return l -- a loop has been found
findLoops arrows visited -- continue looking for unvisited nodes
where
visitSuccessors (x : Name) (x0 : Name) (visited0 : HashSet Name) : Sum (HashSet Name) (List Name) := Id.run do
let mut visited : HashSet Name := visited0
let directSuccessors := arrows.findD x {}
for y in directSuccessors do
if y == x0 then
return .inr [x] -- loop found
if visited.contains y then
continue -- no loop possible here because the visited nodes do not lead to x0
visited := visited.insert y
match visitSuccessors y x0 visited with
| .inl visited' => visited := visited'
| .inr l => return .inr (x :: l)
return .inl visited
/-- Build the game. This command will precompute various things about the game, such as which /-- Build the game. This command will precompute various things about the game, such as which
tactics are available in each level etc. -/ tactics are available in each level etc. -/
elab "MakeGame" : command => do elab "MakeGame" : command => do
@ -668,12 +733,12 @@ elab "MakeGame" : command => do
content := content content := content
}) })
-- Compute which inventory items are available in which level: -- Compute which inventory items are available in which level:
for inventoryType in #[.Tactic, .Definition, .Lemma] do for inventoryType in #[.Tactic, .Definition, .Lemma] do
-- Which items are introduced in which world? -- Which items are introduced in which world?
let mut newItemsInWorld : HashMap Name (HashSet Name) := {} let mut newItemsInWorld : HashMap Name (HashSet Name) := {}
let mut worldsWithNewItem : HashMap Name (HashSet Name) := {}
let mut lemmaStatements : HashMap (Name × Nat) Name := {} let mut lemmaStatements : HashMap (Name × Nat) Name := {}
let mut allItems : HashSet Name := {} let mut allItems : HashSet Name := {}
for (worldId, world) in game.worlds.nodes.toArray do for (worldId, world) in game.worlds.nodes.toArray do
@ -709,27 +774,6 @@ elab "MakeGame" : command => do
newItems := newItems.insert name newItems := newItems.insert name
allItems := allItems.insert name allItems := allItems.insert name
newItemsInWorld := newItemsInWorld.insert worldId newItems newItemsInWorld := newItemsInWorld.insert worldId newItems
for newItem in newItems do
worldsWithNewItem := worldsWithNewItem.insert newItem $
(worldsWithNewItem.findD newItem {}).insert worldId
-- Which items are used in which world?
let mut usedItemsInWorld : HashMap Name (HashSet Name) := {}
for (worldId, world) in game.worlds.nodes.toArray do
let mut usedItems : HashSet Name := {}
for (levelId, level) in world.levels.toArray do
usedItems := usedItems.insertMany (level.getInventory inventoryType).used
usedItemsInWorld := usedItemsInWorld.insert worldId usedItems
let mut worldDependsOnWorlds : HashMap Name (HashSet Name) := {}
for (dependentWorldId, dependentWorld) in game.worlds.nodes.toArray do
let mut dependsOnWorlds : HashSet Name := {}
for usedItem in usedItemsInWorld.find! dependentWorldId do
match worldsWithNewItem.find? usedItem with
| some worldId => dependsOnWorlds := dependsOnWorlds.insertMany worldId
| none => logWarning m!"No world introducing {usedItem}, but required by {dependentWorldId}"
worldDependsOnWorlds := worldDependsOnWorlds.insert dependentWorldId dependsOnWorlds
logInfo m!"World {dependentWorldId} depends on {dependsOnWorlds.toArray}"
-- Basic inventory item availability: all locked. -- Basic inventory item availability: all locked.
let Availability₀ : HashMap Name InventoryTile := let Availability₀ : HashMap Name InventoryTile :=
@ -805,6 +849,52 @@ elab "MakeGame" : command => do
return level.setComputedInventory inventoryType itemsArray return level.setComputedInventory inventoryType itemsArray
-- Which items are used/new in which world?
let mut usedItemsInWorld : HashMap Name (HashSet Name) := {}
let mut newItemsInWorld : HashMap Name (HashSet Name) := {}
for (worldId, world) in game.worlds.nodes.toArray do
let mut usedItems : HashSet Name := {}
let mut newItems : HashSet Name := {}
for inventoryType in #[.Tactic, .Definition, .Lemma] do
for (levelId, level) in world.levels.toArray do
usedItems := usedItems.insertMany (level.getInventory inventoryType).used
newItems := newItems.insertMany (level.getInventory inventoryType).new
usedItemsInWorld := usedItemsInWorld.insert worldId usedItems
newItemsInWorld := newItemsInWorld.insert worldId newItems
let mut worldsWithNewItem : HashMap Name (HashSet Name) := {}
for (worldId, world) in game.worlds.nodes.toArray do
for newItem in newItemsInWorld.find! worldId do
worldsWithNewItem := worldsWithNewItem.insert newItem $
(worldsWithNewItem.findD newItem {}).insert worldId
-- which world depends on which world?
let mut worldDependsOnWorlds : HashMap Name (HashSet Name) := {}
let mut dependencyReason : HashMap (Name × Name) Name := {}
for (dependentWorldId, dependentWorld) in game.worlds.nodes.toArray do
let mut dependsOnWorlds : HashSet Name := {}
for usedItem in usedItemsInWorld.find! dependentWorldId do
match worldsWithNewItem.find? usedItem with
| some worldIds =>
if !worldIds.contains dependentWorldId then
dependsOnWorlds := dependsOnWorlds.insertMany worldIds
for worldId in worldIds do
dependencyReason := dependencyReason.insert (dependentWorldId, worldId) usedItem
| none => logWarning m!"No world introducing {usedItem}, but required by {dependentWorldId}"
worldDependsOnWorlds := worldDependsOnWorlds.insert dependentWorldId dependsOnWorlds
let loop := findLoops worldDependsOnWorlds
if loop != [] then
logError m!"Loop: Dependency graph has a loop: {loop}"
for i in [:loop.length] do
let w1 := loop[i]!
let w2 := loop[if i == loop.length - 1 then 0 else i + 1]!
let item := dependencyReason.find! (w1, w2)
logError m!"{w1} depends on {w2} because of {item}"
else
worldDependsOnWorlds ← removeTransitive worldDependsOnWorlds
logInfo m!"Dependencies: {worldDependsOnWorlds.toArray.map fun (a,b) => (a,b.toArray)}"
/-! # Debugging tools -/ /-! # Debugging tools -/

Loading…
Cancel
Save