Graph Representation#
Module defining the nodes and edges of the graph representing the lymphatic system.
Anything related to the network of nodes and edges is defined here. This includes the
nodes themselves (either Tumor or LymphNodeLevel), the edges
(Edge), and the graph (Representation).
The nodes and edges are used to define the structure of the graph, which may then be
accessed via the Representation class. This in turn is then used to
compute e.g. the transition matrix of the model.
- class lymph.graph.AbstractNode(name: str, state: int, allowed_states: list[int] | None = None)[source]#
Bases:
objectAbstract base class for nodes in the graph reprsenting the lymphatic system.
- __init__(name: str, state: int, allowed_states: list[int] | None = None) None[source]#
Make a new node.
Upon initialization, the
nameandstateof the node must be provided. Thestatemust be one of theallowed_states. The constructor makes sure that theallowed_statesare a list of ints, even when, e.g., a tuple of floats is provided.
- property name: str#
Return the name of the node.
- property state: int#
Return the state of the node.
- comp_obs_prob(obs: int, obs_table: ndarray, log: bool = False) float[source]#
Compute the probability of the diagnosis
obs, given the current state.The
obs_tableis a 2D array with the rows corresponding to the states and the columns corresponding to the observations. It encodes for each state and diagnosis the corresponding probability.
- class lymph.graph.Tumor(name: str, state: int = 1)[source]#
Bases:
AbstractNodeA tumor in the graph representation of the lymphatic system.
- class lymph.graph.LymphNodeLevel(name: str, state: int = 0, allowed_states: list[int] | None = None)[source]#
Bases:
AbstractNodeA lymph node level (LNL) in the graph representation of the lymphatic system.
- __init__(name: str, state: int = 0, allowed_states: list[int] | None = None) None[source]#
Create a new lymph node level.
- classmethod binary(name: str, state: int = 0) LymphNodeLevel[source]#
Create a new binary LNL.
- classmethod trinary(name: str, state: int = 0) LymphNodeLevel[source]#
Create a new trinary LNL.
- property is_binary: bool#
Return whether the node is binary.
- property is_trinary: bool#
Return whether the node is trinary.
- class lymph.graph.Edge(parent: Tumor | LymphNodeLevel, child: LymphNodeLevel, spread_prob: float = 0.0, micro_mod: float = 1.0)[source]#
Bases:
objectRepresentation of an arc in the graph representation of the lymph system.
- __init__(parent: Tumor | LymphNodeLevel, child: LymphNodeLevel, spread_prob: float = 0.0, micro_mod: float = 1.0) None[source]#
Create a new edge between two nodes.
The
parentnode must be aTumoror aLymphNodeLevel, and thechildnode must be aLymphNodeLevel.The
spread_probparameter is the probability of a tumor or involved LNL to spread to the next LNL. Themicro_modparameter is a modifier for the spread probability in case of only a microscopic node involvement.
- property parent: Tumor | LymphNodeLevel#
Return the parent node that drains lymphatically via the edge.
- property child: LymphNodeLevel#
Return the child node of the edge, receiving lymphatic drainage.
- get_name(middle='to') str[source]#
Return the name of the edge.
An edge’s name is simply the name of the parent node and the child node, connected by the string provided via the
middleargument.This is used to identify and assign spread probabilities to it e.g. in the
set_params()method and elsewhere.>>> lnl_II = LymphNodeLevel("II") >>> lnl_III = LymphNodeLevel("III") >>> edge = Edge(lnl_II, lnl_III) >>> edge.get_name() 'IItoIII' >>> edge.get_name(middle='->') 'II->III'
- property is_growth: bool#
Check if this edge represents a node’s growth.
- property is_tumor_spread: bool#
Check if this edge represents spread from a tumor to an LNL.
- set_micro_mod(new_micro_mod: float | None) None[source]#
Set the spread modifier for LNLs with microscopic involvement.
- property micro_mod: float#
Parameter modifying spread probability in case of macroscopic involvement
- set_spread_prob(new_spread_prob: float | None) None[source]#
Set the spread probability of the edge.
- property spread_prob: float#
Spread probability of the edge
- get_params(as_dict: bool = True, **_kwargs) Iterable[float] | dict[str, float][source]#
Return the value of the parameter
paramor all params in a dict.
- set_params(*args, **kwargs) tuple[float][source]#
Set the values of the edge’s parameters.
If provided as positional arguments, the edge connects to a trinary node, and is not a growth node, the first argument is the spread probability and the second argument is the microscopic spread modifier. Otherwise it only consumes one argument, which is the growth or spread probability.
Keyword arguments (i.e.,
"growth","spread", and"micro") override positional arguments. Unused args are returned.>>> edge = Edge( ... LymphNodeLevel("II", allowed_states=[0, 1, 2]), ... LymphNodeLevel("III"), ... ) >>> _ = edge.set_params(0.1, 0.2) >>> edge.spread_prob 0.1 >>> edge.micro_mod 0.2 >>> _ = edge.set_params(spread=0.3, micro=0.4) >>> edge.spread_prob 0.3 >>> edge.micro_mod 0.4
- property transition_tensor: ndarray#
Return the transition tensor of the edge.
- class lymph.graph.Representation(graph_dict: dict[tuple[str], list[str]], tumor_state: int | None = None, allowed_states: list[int] | None = None)[source]#
Bases:
objectClass holding the graph structure of the model.
This class allows accessing the connected nodes (
TumorandLymphNodeLevel) and edges (Edge) of themodels.- __init__(graph_dict: dict[tuple[str], list[str]], tumor_state: int | None = None, allowed_states: list[int] | None = None) None[source]#
Create a new graph representation of nodes and edges.
The
graph_dictis a dictionary that defines which nodes are created and with what edges they are connected. The keys of the dictionary are tuples of the form(node_type, node_name). Thenode_typecan be either"tumor"or"lnl". Thenode_nameis a string that uniquely identifies the node. The values of the dictionary are lists of node names to which the key node should be connected.
- property nodes: dict[str, Tumor | LymphNodeLevel]#
List of both
TumorandLymphNodeLevelinstances.
- property lnls: dict[str, LymphNodeLevel]#
List of all
LymphNodeLevelnodes in the graph.
- property allowed_states: list[int]#
Return the list of allowed states for each
LymphNodeLevel.
- property is_binary: bool#
Indicate if the model is binary.
Returns
Trueif allLymphNodeLevelinstances are binary,Falseotherwise.
- property is_trinary: bool#
Returns
Trueif the graph is trinary,Falseotherwise.Similar to
is_binary().
- property tumor_edges: dict[str, Edge]#
List of all tumor
Edgeinstances in the graph.This contains all edges who’s parents are instances of
Tumorand who’s children are instances ofLymphNodeLevel.
- property lnl_edges: dict[str, Edge]#
List of all LNL
Edgeinstances in the graph.This contains all edges who’s parents and children are instances of
LymphNodeLevel, including growth edges (if the graph is trinary).
- property growth_edges: dict[str, Edge]#
List of all growth
Edgeinstances in the graph.Growth edges are only present in trinary models and are arcs where the parent and child are the same
LymphNodeLevelinstance. They facilitate the change from a micsoscopically positive to a macroscopically positive LNL.
- to_dict() dict[tuple[str, str], set[str]][source]#
Return graph representing this instance’s nodes and egdes as dictionary.
>>> graph_dict = { ... ('tumor', 'T'): ['II', 'III'], ... ('lnl', 'II'): ['III'], ... ('lnl', 'III'): [], ... } >>> graph = Representation(graph_dict) >>> graph.to_dict() == graph_dict True
- get_mermaid(with_params: bool = True, direction: Literal['TD', 'LR'] = 'TD') str[source]#
Print the graph in mermaid format.
>>> graph_dict = { ... ('tumor', 'T'): ['II', 'III'], ... ('lnl', 'II'): ['III'], ... ('lnl', 'III'): [], ... } >>> graph = Representation(graph_dict) >>> graph.edges["TtoII"].spread_prob = 0.1 >>> graph.edges["TtoIII"].spread_prob = 0.2 >>> graph.edges["IItoIII"].spread_prob = 0.3 >>> print(graph.get_mermaid()) flowchart TD T-->|10%| II T-->|20%| III II-->|30%| III >>> print(graph.get_mermaid(with_params=False)) flowchart TD T--> II T--> III II--> III
- get_mermaid_url(**mermaid_kwargs) str[source]#
Return the URL to the rendered graph.
Keyword arguments are passed to
get_mermaid().
- get_state(as_dict: bool = False) dict[str, int] | list[int][source]#
Return the states of the system’s LNLs.
If
as_dictisTrue, the result is a dictionary with the names of the LNLs as keys and their states as values. Otherwise, the result is a list of the states of the LNLs in the order they appear in the graph.
- set_state(*new_states_args, **new_states_kwargs) None[source]#
Assign a new state to the system’s LNLs.
The state can either be provided with positional arguments or as keyword arguments. In case of positional arguments, the order must be the same as the order of the LNLs in the graph. If keyword arguments are used, the keys must be the names of the LNLs. The order of the keyword arguments does not matter.
The keyword arguments override the positional arguments.
- property state_list#
Return list of all possible hidden states.
E.g., for three binary LNLs I, II, III, the first state would be where all LNLs are in state 0. The second state would be where LNL III is in state 1 and all others are in state 0, etc. The third represents the case where LNL II is in state 1 and all others are in state 0, etc. Essentially, it looks like binary counting:
>>> graph = Representation(graph_dict={ ... ("tumor", "T"): ["I", "II" , "III"], ... ("lnl", "I"): [], ... ("lnl", "II"): ["I", "III"], ... ("lnl", "III"): [], ... }) >>> graph.state_list array([[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]])
- get_params(as_dict: bool = True, as_flat: bool = True) Iterable[float] | dict[str, float][source]#
Return the parameters of the edges in the graph.
If
as_dictisFalse, return an iterable of all parameter values. Ifas_dictisTrue, return a nested dictionary with the edges’ names as keys and the edges’ parameter dicts as values.If
as_flatisTrue, return a flat dictionary with the T-stages and parameters as keys and values, respectively. This is the result of passing the nested dictionary toflatten().
- set_params(*args, **kwargs) tuple[float][source]#
Set the parameters of the edges in the graph.
The arguments are passed to the
set_params()method of the edges. Global keyword arguments (e.g."spread") are passed to each edge’sset_paramsmethod. Unused args are returned.Specific keyword arguments take precedence over global ones which in turn take precedence over positional arguments.
>>> graph = Representation(graph_dict={ ... ("tumor", "T"): ["II" , "III"], ... ("lnl", "II"): ["III"], ... ("lnl", "III"): [], ... }) >>> _ = graph.set_params(0.1, 0.2, 0.3, spread=0.4, TtoII_spread=0.5) >>> graph.get_params(as_dict=True) {'TtoII_spread': 0.5, 'TtoIII_spread': 0.4, 'IItoIII_spread': 0.4}