Source code for evokit.evolvables.gp_visualiser

# mypy: disable-error-code="import-untyped,no-any-unimported"
from graphviz import Digraph

from .gp import Expression

from typing import Callable
from .gp import Program
#: Global counter of the number of dispatched identifiers.
ident = 0


def _dispatch_ident() -> str:
    """Return an unique identifier.

    During the same runtime, each call of this method returns a
    different identifier.
    """
    global ident
    return "a" + str(*(ident := ident + 1,))


[docs] def p2dot(gp: Program, dispatcher: Callable[[], str] = _dispatch_ident) -> Digraph: """Visualise a tree-based genetic program. Return a :class:`graphviz.Digraph` that represents the given tree-based genetic program. Args: gp: Genetic program to visualise. dispatcher: :class:`Callable` that should return a unique identifier when called. """ expr: Expression = gp.genome my_name: str = expr.value.__name__ if callable(expr.value) else str(expr.value) my_ident: str = dispatcher() dot: Digraph = Digraph("GP Visualisation") dot.node(my_ident, my_name) for each_child in expr.children: _p2dot_recurse(each_child, dot, my_ident, dispatcher) return dot
def _p2dot_recurse(expr: Expression, dot: Digraph, parent_ident: str, dispatcher: Callable[[], str]) -> None: """Recursive function that builds the visualisation. Recursively add nodes to a :class:`graphviz.Digraph`, then return it. Args: expr: An :class:`.Expression` dot: A :class:`graphviz.Digraph` parent_ident: Identifier of the parent node dispatcher: :class:`Callable` that should return a unique identifier when called. """ my_name: str = expr.value.__name__ if callable(expr.value) else str(expr.value) my_ident: str = dispatcher() dot.node(my_ident, my_name) dot.edge(parent_ident, my_ident) for each_child in expr.children: _p2dot_recurse(each_child, dot, my_ident, dispatcher)