Analysis functions for Choreo.MindMap.
Provides algorithms that answer practical questions about a mind map:
- How deep is the map? (max depth from root)
- How broad is the map? (number of leaf nodes)
- Which ideas are orphaned? (not reachable from root)
- What are all root-to-leaf paths?
- Is the map structurally sound?
Further reading
Summary
Functions
Returns the number of leaf nodes (nodes with no outgoing branch edges).
Checks whether the mind map contains a directed cycle via branch edges.
Returns the maximum depth of the mind map (number of branch edges from root to deepest leaf).
Returns all leaf node IDs (nodes with no outgoing branch edges).
Returns the maximum number of nodes at any single depth level.
Returns nodes that are not reachable from the root via branch edges.
Enumerates all root-to-leaf paths via branch edges.
Returns a map of node type frequencies.
Validates mind map structural soundness.
Functions
@spec breadth(Choreo.MindMap.t()) :: non_neg_integer()
Returns the number of leaf nodes (nodes with no outgoing branch edges).
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a)
...> |> Choreo.MindMap.add_topic(:b)
...> |> Choreo.MindMap.add_topic(:c)
...> |> Choreo.MindMap.branch(:a, :b)
...> |> Choreo.MindMap.branch(:a, :c)
iex> Choreo.MindMap.Analysis.breadth(map)
2This analysis answers the question: "How many leaf ideas exist?"
@spec cyclic?(Choreo.MindMap.t()) :: boolean()
Checks whether the mind map contains a directed cycle via branch edges.
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a)
...> |> Choreo.MindMap.add_topic(:b)
...> |> Choreo.MindMap.add_topic(:c)
...> |> Choreo.MindMap.branch(:a, :b)
...> |> Choreo.MindMap.branch(:b, :c)
...> |> Choreo.MindMap.branch(:c, :b)
iex> Choreo.MindMap.Analysis.cyclic?(map)
true
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a)
...> |> Choreo.MindMap.add_topic(:b)
...> |> Choreo.MindMap.branch(:a, :b)
iex> Choreo.MindMap.Analysis.cyclic?(map)
falseThis analysis answers the question: "Is there a cycle in the hierarchy?"
@spec depth(Choreo.MindMap.t()) :: non_neg_integer()
Returns the maximum depth of the mind map (number of branch edges from root to deepest leaf).
A single-node map has depth 0.
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a, label: "a")
...> |> Choreo.MindMap.add_topic(:b)
...> |> Choreo.MindMap.add_subtopic(:c)
...> |> Choreo.MindMap.branch(:a, :b)
...> |> Choreo.MindMap.branch(:b, :c)
iex> Choreo.MindMap.Analysis.depth(map)
2
iex> Choreo.MindMap.Analysis.depth(Choreo.MindMap.new())
0This analysis answers the question: "How deep is the mind map?"
@spec leaves(Choreo.MindMap.t()) :: [Yog.node_id()]
Returns all leaf node IDs (nodes with no outgoing branch edges).
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a)
...> |> Choreo.MindMap.add_topic(:b)
...> |> Choreo.MindMap.add_topic(:c)
...> |> Choreo.MindMap.branch(:a, :b)
...> |> Choreo.MindMap.branch(:a, :c)
iex> Enum.sort(Choreo.MindMap.Analysis.leaves(map))
[:b, :c]This analysis answers the question: "Which ideas have no children?"
@spec max_width(Choreo.MindMap.t()) :: non_neg_integer()
Returns the maximum number of nodes at any single depth level.
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a)
...> |> Choreo.MindMap.add_topic(:b)
...> |> Choreo.MindMap.add_topic(:c)
...> |> Choreo.MindMap.add_topic(:d)
...> |> Choreo.MindMap.branch(:a, :b)
...> |> Choreo.MindMap.branch(:a, :c)
...> |> Choreo.MindMap.branch(:a, :d)
iex> Choreo.MindMap.Analysis.max_width(map)
3This analysis answers the question: "What is the widest level of the map?"
@spec orphan_nodes(Choreo.MindMap.t()) :: [Yog.node_id()]
Returns nodes that are not reachable from the root via branch edges.
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a)
...> |> Choreo.MindMap.add_topic(:b)
...> |> Choreo.MindMap.add_topic(:c)
...> |> Choreo.MindMap.branch(:a, :b)
iex> Choreo.MindMap.Analysis.orphan_nodes(map)
[:c]This analysis answers the question: "Which ideas are disconnected from the root?"
@spec paths(Choreo.MindMap.t()) :: [[Yog.node_id()]]
Enumerates all root-to-leaf paths via branch edges.
Each path is a list of node IDs from root to leaf.
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a)
...> |> Choreo.MindMap.add_topic(:b)
...> |> Choreo.MindMap.add_subtopic(:c)
...> |> Choreo.MindMap.branch(:a, :b)
...> |> Choreo.MindMap.branch(:b, :c)
iex> Choreo.MindMap.Analysis.paths(map)
[[:a, :b, :c]]This analysis answers the question: "What are all root-to-leaf paths?"
@spec type_frequencies(Choreo.MindMap.t()) :: %{required(atom()) => non_neg_integer()}
Returns a map of node type frequencies.
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a)
...> |> Choreo.MindMap.add_topic(:b)
...> |> Choreo.MindMap.add_subtopic(:c)
...> |> Choreo.MindMap.add_note(:d)
iex> Choreo.MindMap.Analysis.type_frequencies(map)
%{root: 1, topic: 1, subtopic: 1, note: 1}This analysis answers the question: "What is the composition of the map?"
@spec validate(Choreo.MindMap.t()) :: [{:error | :warning, String.t()}]
Validates mind map structural soundness.
Checks for:
- missing root
- cycles in the hierarchy
- orphan nodes (not reachable from root)
- nodes with multiple branch parents
Returns a list of {severity, message} tuples.
Examples
iex> map = Choreo.MindMap.new()
iex> map = map
...> |> Choreo.MindMap.set_root(:a)
...> |> Choreo.MindMap.add_topic(:b)
...> |> Choreo.MindMap.branch(:a, :b)
iex> Choreo.MindMap.Analysis.validate(map)
[]
iex> map = Choreo.MindMap.new()
iex> Choreo.MindMap.Analysis.validate(map)
[{:error, "Mind map has no root"}]This analysis answers the question: "Is the mind map structurally sound?"