Source code for molfoundry.dg

from typing import Any, List, Optional, Tuple, Union

from .dgvertexmapper import DGVertexMapper
from .derivation import Derivations
from .graphprinter import GraphPrinter
from .exceptions import *
from .labelrelation import LabelRelation
from .labeltype import LabelType
from .labelsettings import LabelSettings
from .post import summaryRaw
from .rule import Rule
from .graph import Graph, VertexFilter
from .dgstrat import DGStrat, GraphState
from .dgprintdata import DGPrintData
from .core import TSObject

_class_ref_DG = TSObject.get_class_ref("DG")
_class_ref_DGBuilder = TSObject.get_class_ref("DGBuilder")
_class_ref_DGVertex = TSObject.get_class_ref("DGVertex")
_class_ref_DGHyperEdge = TSObject.get_class_ref("DGHyperEdge")


# noinspection PyPep8Naming
[docs] class DG(TSObject): def __init__(self, labelSettings=LabelSettings(LabelType.String, LabelRelation.Isomorphism), graphDatabase: List[Graph] = []) -> None: if any(x is None for x in graphDatabase): raise LogicError("Null pointer in graph database.") for i, g1 in enumerate(graphDatabase): for j, g2 in enumerate(graphDatabase): if i != j and g1.canonKey == g2.canonKey: raise LogicError("Isomorphic graphs '%s' and '%s' in initial graph database." % (g1.name, g2.name)) super().__init__(_class_ref_DG, labelSettings._ref, [g._ref for g in graphDatabase]) @property def id(self) -> int: return int(self._ref.getId()) @property def labelSettings(self) -> LabelSettings: return LabelSettings.wrap(self._ref.getLabelSettings()) @property def graphDatabase(self) -> List[Graph]: return [Graph.wrap(g) for g in self._ref.getGraphDatabase()] @property def createdGraphs(self) -> List[Graph]: return [Graph.wrap(g) for g in self._ref.getCreatedGraphs()] @property def locked(self) -> bool: return self._ref.isLocked() @property def hasActiveBuilder(self) -> bool: return self._ref.hasActiveBuilder() @property def numVertices(self) -> int: return TSObject.wrap_exception(lambda: int(self._ref.getNumVertices())) @property def vertices(self) -> List["DG.Vertex"]: return TSObject.wrap_exception(lambda: [DG.Vertex.wrap(v) for v in self._ref.getVertices()]) @property def numEdges(self) -> int: return TSObject.wrap_exception(lambda: int(self._ref.getNumEdges())) @property def edges(self) -> List["DG.HyperEdge"]: return TSObject.wrap_exception(lambda: [DG.HyperEdge.wrap(e) for e in self._ref.getEdges()])
[docs] def findVertex(self, g: Graph) -> "DG.Vertex": return DG.Vertex.wrap(self._ref.findVertex(g._ref))
[docs] def findEdge(self, sources: List[Union[Graph, "DG.Vertex"]], targets: List[Union[Graph, "DG.Vertex"]]) -> "DG.HyperEdge": return DG.HyperEdge.wrap(self._ref.findEdge([x._ref for x in sources], [x._ref for x in targets]))
[docs] def build(self) -> "DG.Builder": return TSObject.wrap_exception(lambda: DG.Builder.wrap(self._ref.build()))
[docs] def print( self, printer: Optional[GraphPrinter] = None, data: Optional[DGPrintData] = None, ): """ Emit a static SVG visualization of the derivation graph. Every DG vertex renders as an oval-framed molecule depiction (produced by the shared `GraphSVGDrawer`, in the mode / collapse-H setting picked up from `printer`) with the graph's name captioned inside the oval. Every hyperedge with a single source and a single target collapses to a direct arrow labeled with `e{id}`; every multi-educt or multi-product hyperedge gets a small labeled square "junction" node with arrows from the educts into the junction and from the junction to the products. Node positions come from a force-directed layout so junctions settle between their attached molecules; each oval's dimensions match the aspect ratio of its molecule SVG's viewBox so nothing gets stretched. Everything is one inline SVG, so the dynamic light/dark theme follows the outer page's `[data-bs-theme]` via CSS without needing any JavaScript. """ printer_ref = printer._ref if printer is not None else None svg = TSObject.wrap_exception( lambda: self._ref.getSVG(printer_ref) ) title = f"DG {self.id} ({self.numVertices} vertices, {self.numEdges} hyperedges)" summaryRaw(f"""<div class="card mb-4" style="max-width: 1200px; margin: auto"> <div class="d-flex m-2"> <h5 class="card-title flex-grow-1 mb-0">{title}</h5> </div> <div class="m-2"> {svg} </div> </div>""")
def __str__(self): return self._ref.toString() def __repr__(self): return self._ref.toString() # noinspection PyPep8Naming
[docs] class Vertex(TSObject): def __init__(self) -> None: super().__init__(_class_ref_DGVertex, None, -1, None)
[docs] def isNull(self) -> bool: return self._ref.isNull()
def __bool__(self): return not self._ref.isNull() @property def id(self) -> int: return TSObject.wrap_exception(lambda: int(self._ref.getId())) @property def dg(self) -> "DG": return TSObject.wrap_exception(lambda: DG.wrap(self._ref.getDG())) @property def inDegree(self) -> int: return TSObject.wrap_exception(lambda: int(self._ref.getInDegree())) @property def inEdges(self) -> List["DG.HyperEdge"]: return TSObject.wrap_exception(lambda: [DG.HyperEdge.wrap(e) for e in self._ref.getInEdges()]) @property def outDegree(self) -> int: return TSObject.wrap_exception(lambda: int(self._ref.getOutDegree())) @property def outEdges(self) -> List["DG.HyperEdge"]: return TSObject.wrap_exception(lambda: [DG.HyperEdge.wrap(e) for e in self._ref.getOutEdges()]) @property def graph(self) -> Graph: return TSObject.wrap_exception(lambda: Graph.wrap(self._ref.getGraph()))
# noinspection PyPep8Naming
[docs] class HyperEdge(TSObject): def __init__(self) -> None: super().__init__(_class_ref_DGHyperEdge, None, -1, [], [], []) def __str__(self): return self._ref.toString() def __repr__(self): return self._ref.toString()
[docs] def isNull(self) -> bool: return self._ref.isNull()
def __bool__(self): return not self._ref.isNull() @property def id(self) -> int: return TSObject.wrap_exception(lambda: int(self._ref.getId())) @property def dg(self) -> "DG": return TSObject.wrap_exception(lambda: DG.wrap(self._ref.getDG())) @property def numSources(self) -> int: return TSObject.wrap_exception(lambda: int(self._ref.getNumSources())) @property def sources(self) -> List["DG.Vertex"]: return TSObject.wrap_exception(lambda: [DG.Vertex.wrap(v) for v in self._ref.getSources()]) @property def numTargets(self) -> int: return TSObject.wrap_exception(lambda: int(self._ref.getNumTargets())) @property def targets(self) -> List["DG.Vertex"]: return TSObject.wrap_exception(lambda: [DG.Vertex.wrap(v) for v in self._ref.getTargets()]) @property def rules(self) -> List[Rule]: return TSObject.wrap_exception(lambda: [Rule.wrap(r) for r in self._ref.getRules()]) @property def inverse(self) -> "DG.HyperEdge": return TSObject.wrap_exception(lambda: DG.HyperEdge.wrap(self._ref.getInverse()))
[docs] def vertexMaps(self, limit: int = 1, vertex_filter: Optional[VertexFilter] = None): return DGVertexMapper(self, limit, vertex_filter)
def __eq__(self, other): if not isinstance(other, DG.HyperEdge): return NotImplemented return self._ref.equals(other._ref)[0]
[docs] def equals(self, other: "DG.HyperEdge") -> Tuple[bool, bool]: return self._ref.equals(other._ref)
[docs] def print(self, printer: Optional[GraphPrinter] = None): svgs: List[str] = TSObject.wrap_exception(lambda: self._ref.getSVG()) title = f"{{{', '.join(s.graph.name for s in self.sources)}}} -> {{{', '.join(t.graph.name for t in self.targets)}}}" for svg in svgs: summaryRaw(f"""<div class="card mb-4" style="max-width: 800px; margin: auto"> <div class="d-flex m-2"> <h5 class="card-title flex-grow-1 mb-0">Derivation {title}</h5> </div> {svg} </div>""")
# noinspection PyPep8Naming
[docs] class Builder(TSObject): def __init__(self, dg: "DG") -> None: super().__init__(_class_ref_DGBuilder, dg._ref) @property def dg(self) -> "DG": return self._ref.getDG() @property def isActive(self) -> bool: return self._ref.isActive() def __del__(self): self._ref.lock() def __enter__(self): return self def __exit__(self, *_: Any) -> None: self._ref.lock()
[docs] def execute(self, strategy: DGStrat, verbosity: int = 2, ignoreRuleLabelTypes=False) -> "ExecuteResult": """ :param strategy: The strategy to execute :param verbosity: The verbosity defaults to level 2. The levels have the following meaning: * 0 (or less): no information is printed. * 2: Repetition strategies print information for each round. * 4: All strategies print minimal information. * 6: Derivation predicate strategies and filtering strategies also print their predicates. * 8: Rule strategies print minimal information about graph binding. * 10: Rule strategies print more information about graph binding, including failure due to derivation predicates. * 50: Print information about morphism generation for rule composition. * 60: Print rule composition information. :param ignoreRuleLabelTypes: Whether rules in the strategy should be checked beforehand for whether they have an associated LabelType which matches the one in the underlying derivation graph. """ return GraphState.wrap(self._ref.execute(strategy._ref, verbosity, ignoreRuleLabelTypes))
[docs] def apply(self, graphs: List[Graph], r: Rule, onlyProper=True, verbosity=0) -> List["DG.HyperEdge"]: return [DG.HyperEdge.wrap(e) for e in self._ref.apply([g._ref for g in graphs], r._ref, onlyProper, verbosity)]
[docs] def addDerivation(self, d: Derivations) -> "DG.HyperEdge": return DG.HyperEdge.wrap(self._ref.addDerivation(d._ref))
[docs] class ExecuteResult: def __init__(self, state: GraphState): self._state = state
[docs] def subset(self) -> List[Graph]: return self._state.subset
[docs] def universe(self) -> List[Graph]: return self._state.universe