Module clu.phontools.alignment.parser.graph
Expand source code
from __future__ import annotations
from .symbols import Symbol, TranscriptTypes
from .actions import Actions
from dataclasses import dataclass, field
from typing import List
from networkx.drawing.nx_pydot import to_pydot
from networkx.drawing.nx_agraph import to_agraph
import pygraphviz as pgv
import networkx as nx
import pydot
__all__ = ["Graph", "Edge"]
@dataclass
class Graph:
"""An alignment graph mapping transcribed phones to gold phones along with the relationship (deletion, substitution, alignment, etc.)"""
edges: List[Edge] = field(default_factory=list)
def __post_init__(self):
ps2children: Dict[Symbol, List[Symbol]] = dict()
G = nx.DiGraph()
for edge in self.edges:
G.add_nodes_from(
[self._nx_make_node(edge.source), self._nx_make_node(edge.destination)]
)
G.add_edge(
self._nx_make_node_id(edge.source),
self._nx_make_node_id(edge.destination),
label=edge.label.simple(),
)
if edge.source != edge.destination:
children = ps2children.get(edge.source, [])
children.append(edge.destination)
ps2children[edge.source] = children
# mapping of node to its children
self._children: Dict[Symbol, List[Symbol]] = ps2children
# networkx DiGraph
self.G = G
def children_for(self, ps: Symbol) -> List[Symbol]:
"""Retrieves children for the provided Symbol ps."""
return self._children.get(ps, [])
def has_children(self, ps: Symbol) -> bool:
"""Checks whether provided Symbol ps has children."""
return ps.source in self._children
def copy(self) -> Graph:
"""Creates a copy of the graph at this stage"""
return Graph(edges=edges[:])
def _nx_make_node_id(self, ps: Symbol) -> Text:
return f"{str(ps.source)}-{ps.symbol}-{ps.index}"
def _nx_make_node(self, ps: Symbol) -> Tuple[Text, Dict[Text, Text]]:
return (
self._nx_make_node_id(ps),
{
"label": ps.symbol,
"shape": "box" if ps.source == TranscriptTypes.GOLD else "circle",
},
)
def _dot_with_tiered_view(self, G: nx.Graph) -> Text:
"""converts nx.Graph to pygraphviz.AGraph,
add subgraphs for a tiered view,
and returns dot"""
AG = to_agraph(G)
gold = set()
transcribed = set()
for edge in self.edges:
for node in (edge.source, edge.destination):
nx_id = self._nx_make_node_id(node)
if node.source == TranscriptTypes.GOLD:
gold.add(nx_id)
elif node.source == TranscriptTypes.TRANSCRIPT:
transcribed.add(nx_id)
AG.add_subgraph(sorted(list(gold)), rank="min")
AG.add_subgraph(sorted(list(transcribed)), rank="max")
return AG.string()
def to_dot(self) -> Text:
"""Dot-based representation of the alignment graph."""
return self._dot_with_tiered_view(self.G)
@dataclass
class Edge:
""""""
source: Symbol
destination: Symbol
label: Actions
def nodes(self) -> List[Symbol]:
"""Distinct nodes (Symbols) in Edge"""
return list(set([source, destination]))
Classes
class Edge (source: Symbol, destination: Symbol, label: Actions)
-
Edge(source: 'Symbol', destination: 'Symbol', label: 'Actions')
Expand source code
class Edge: """""" source: Symbol destination: Symbol label: Actions def nodes(self) -> List[Symbol]: """Distinct nodes (Symbols) in Edge""" return list(set([source, destination]))
Class variables
var destination : Symbol
var label : Actions
var source : Symbol
Methods
def nodes(self) ‑> List[Symbol]
-
Distinct nodes (Symbols) in Edge
Expand source code
def nodes(self) -> List[Symbol]: """Distinct nodes (Symbols) in Edge""" return list(set([source, destination]))
class Graph (edges: List[Edge] = <factory>)
-
An alignment graph mapping transcribed phones to gold phones along with the relationship (deletion, substitution, alignment, etc.)
Expand source code
class Graph: """An alignment graph mapping transcribed phones to gold phones along with the relationship (deletion, substitution, alignment, etc.)""" edges: List[Edge] = field(default_factory=list) def __post_init__(self): ps2children: Dict[Symbol, List[Symbol]] = dict() G = nx.DiGraph() for edge in self.edges: G.add_nodes_from( [self._nx_make_node(edge.source), self._nx_make_node(edge.destination)] ) G.add_edge( self._nx_make_node_id(edge.source), self._nx_make_node_id(edge.destination), label=edge.label.simple(), ) if edge.source != edge.destination: children = ps2children.get(edge.source, []) children.append(edge.destination) ps2children[edge.source] = children # mapping of node to its children self._children: Dict[Symbol, List[Symbol]] = ps2children # networkx DiGraph self.G = G def children_for(self, ps: Symbol) -> List[Symbol]: """Retrieves children for the provided Symbol ps.""" return self._children.get(ps, []) def has_children(self, ps: Symbol) -> bool: """Checks whether provided Symbol ps has children.""" return ps.source in self._children def copy(self) -> Graph: """Creates a copy of the graph at this stage""" return Graph(edges=edges[:]) def _nx_make_node_id(self, ps: Symbol) -> Text: return f"{str(ps.source)}-{ps.symbol}-{ps.index}" def _nx_make_node(self, ps: Symbol) -> Tuple[Text, Dict[Text, Text]]: return ( self._nx_make_node_id(ps), { "label": ps.symbol, "shape": "box" if ps.source == TranscriptTypes.GOLD else "circle", }, ) def _dot_with_tiered_view(self, G: nx.Graph) -> Text: """converts nx.Graph to pygraphviz.AGraph, add subgraphs for a tiered view, and returns dot""" AG = to_agraph(G) gold = set() transcribed = set() for edge in self.edges: for node in (edge.source, edge.destination): nx_id = self._nx_make_node_id(node) if node.source == TranscriptTypes.GOLD: gold.add(nx_id) elif node.source == TranscriptTypes.TRANSCRIPT: transcribed.add(nx_id) AG.add_subgraph(sorted(list(gold)), rank="min") AG.add_subgraph(sorted(list(transcribed)), rank="max") return AG.string() def to_dot(self) -> Text: """Dot-based representation of the alignment graph.""" return self._dot_with_tiered_view(self.G)
Class variables
var edges : List[Edge]
Methods
def children_for(self, ps: Symbol) ‑> List[Symbol]
-
Retrieves children for the provided Symbol ps.
Expand source code
def children_for(self, ps: Symbol) -> List[Symbol]: """Retrieves children for the provided Symbol ps.""" return self._children.get(ps, [])
def copy(self) ‑> Graph
-
Creates a copy of the graph at this stage
Expand source code
def copy(self) -> Graph: """Creates a copy of the graph at this stage""" return Graph(edges=edges[:])
def has_children(self, ps: Symbol) ‑> bool
-
Checks whether provided Symbol ps has children.
Expand source code
def has_children(self, ps: Symbol) -> bool: """Checks whether provided Symbol ps has children.""" return ps.source in self._children
def to_dot(self) ‑> Text
-
Dot-based representation of the alignment graph.
Expand source code
def to_dot(self) -> Text: """Dot-based representation of the alignment graph.""" return self._dot_with_tiered_view(self.G)