Graph#

The synkit.Graph package provides the core graph-based infrastructure used across SynKit. It supports graph construction, matching, canonicalization, and reaction-specific graph formalisms. Most workflows in rule application, mapping validation, and CRN exploration rely on these utilities.

Key submodules include:

  • Matcher — graph isomorphism and subgraph search engines

  • ITS — Internal Transition State (ITS) graph construction and decomposition

  • MTG — Mechanistic Transition Graph generation and exploration

  • FG — graph-native functional-group detection and audit tooling

  • Context — reaction-center expansion for context-aware matching and analysis

Matcher

Isomorphism, subgraph search, and match enumeration for labeled molecular graphs. Powers rule application and equivalence checks.

ITS

Construct and decompose Internal Transition State graphs to isolate reaction centers and represent bond-order changes explicitly.

MTG

Build Mechanistic Transition Graphs from reaction-center ITS graphs to represent stepwise mechanisms and compare pathways.

FG

Detect functional groups directly on SynKit molecular graphs, with hierarchical labels and aromatic ring-system reporting.

Graph Canonicalization#

The class GraphCanonicaliser canonicalises a graph by computing a deterministic relabeling of node indices. By default it employs a Weisfeiler–Lehman (WL) colour-refinement backend (wl_iterations=3) to obtain a consistent canonical form across isomorphic graphs [1].

Canonicalising an ITS graph and verifying isomorphism#
 1from synkit.IO import rsmi_to_its
 2from synkit.Graph.canon_graph import GraphCanonicaliser
 3from synkit.Graph.Matcher.graph_matcher import GraphMatcherEngine
 4
 5canon = GraphCanonicaliser(backend='wl', wl_iterations=3)
 6
 7rsmi = (
 8    '[CH3:1][CH:2]=[O:3].'
 9    '[CH:4]([H:7])([H:8])[CH:5]=[O:6]>>'
10    '[CH3:1][CH:2]=[CH:4][CH:5]=[O:6].'
11    '[O:3]([H:7])([H:8])'
12)
13
14its_graph = rsmi_to_its(rsmi)
15canon_graph = canon.canonicalise_graph(its_graph).canonical_graph
16
17print(its_graph == canon_graph)  # structural relabeling differs
18
19gm = GraphMatcherEngine(backend='nx')
20print(gm.isomorphic(its_graph, canon_graph))  # graph structure is preserved

Example output

False
True

Matcher#

The synkit.Graph.Matcher submodule provides matching engines for labeled graphs:

Example: Graph Isomorphism#

Check whether two ITS graphs—derived from reaction SMILES differing only by atom-map ordering—are isomorphic.

Full-graph isomorphism check with GraphMatcherEngine#
 1from synkit.IO import rsmi_to_its
 2from synkit.Graph.Matcher.graph_matcher import GraphMatcherEngine
 3
 4rsmi_1 = (
 5    '[CH3:1][C:2](=[O:3])[OH:4].[CH3:5][OH:6]'
 6    '>>'
 7    '[CH3:1][C:2](=[O:3])[O:6][CH3:5].[OH2:4]'
 8)
 9rsmi_2 = (
10    '[CH3:5][C:1](=[O:2])[OH:3].[CH3:6][OH:4]'
11    '>>'
12    '[CH3:5][C:1](=[O:2])[O:4][CH3:6].[OH2:3]'
13)
14
15its_1 = rsmi_to_its(rsmi_1)
16its_2 = rsmi_to_its(rsmi_2)
17
18gm = GraphMatcherEngine(
19    backend='nx',
20    node_attrs=['element', 'charge'],
21    edge_attrs=['order'],
22)
23
24are_isomorphic = gm.isomorphic(its_1, its_2)
25print(are_isomorphic)

Example output

True

ITS#

The synkit.Graph.ITS package supports the construction and decomposition of Internal Transition State (ITS) graphs:

  • ITSConstruction — build ITS graphs from reactant/product graphs

  • get_rc() — extract the minimal reaction-center subgraph

  • its_decompose() — split an ITS graph into reactant/product graphs

Lewis State Graph fields#

SynKit 1.4 introduces the Lewis State Graph (LSG) framework for the pure-Python reactor and new mechanistic work. Legacy ITS remains available, but LSG is the preferred representation when valence-state information must be explicit. In the current API this representation is requested with format="tuple".

Important LSG fields:

Field

Meaning

sigma_order / pi_order

Authoritative bond components for Lewis-state rewriting.

kekule_order

Integer-like bond order used for product reconstruction; normally sigma_order + pi_order.

lone_pairs / radical

Valence-state fields used by LSG matching and product accounting.

valence_electrons

Element valence-shell reference used when recomputing charge.

order

Legacy or presentation order. Aromatic 1.5 values are useful for matching and visualization, but not the LSG-authoritative rewrite source.

Building an LSG/ITS graph with Lewis-state fields#
1from synkit.IO import rsmi_to_its
2
3rsmi = "[CH3:1][Cl:2].[NH3:3]>>[CH3:1][NH3+:3].[Cl-:2]"
4its = rsmi_to_its(rsmi, format="tuple", core=False)
5
6print(its.nodes[2]["lone_pairs"])
7print(its.edges[1, 2]["sigma_order"])

Note

Aromatic LSG matching is intentionally conservative. Aromaticity is still useful for presentation and pruning, but full aromatic-system relabeling is tracked as ongoing work.

Example: Construct and Visualize an ITS#

Build an ITS, extract the reaction center, and visualize#
 1from synkit.IO.chem_converter import rsmi_to_graph
 2from synkit.Graph.ITS.its_construction import ITSConstruction
 3from synkit.Graph.ITS.its_decompose import get_rc
 4from synkit.Vis import GraphVisualizer
 5import matplotlib.pyplot as plt
 6
 7rsmi = (
 8    '[CH3:1][CH:2]=[O:3].'
 9    '[CH:4]([H:7])([H:8])[CH:5]=[O:6]'
10    '>>'
11    '[CH3:1][CH:2]=[CH:4][CH:5]=[O:6].'
12    '[O:3]([H:7])([H:8])'
13)
14
15react_graph, prod_graph = rsmi_to_graph(rsmi)
16
17its_graph = ITSConstruction().ITSGraph(react_graph, prod_graph)
18rc_graph = get_rc(its_graph)
19
20vis = GraphVisualizer()
21fig, axes = plt.subplots(1, 2, figsize=(14, 6))
22vis.plot_its(its_graph, axes[0], use_edge_color=True, title='A. Full ITS Graph')
23vis.plot_its(rc_graph, axes[1], use_edge_color=True, title='B. Reaction Center')
24plt.show()
ITS graph and reaction-center of aldol condensation

Figure: (A) Full ITS graph and (B) reaction-center-only ITS graph for the aldol condensation.

MTG Submodule#

The synkit.Graph.MTG package provides tools for constructing and analyzing Mechanistic Transition Graphs (MTGs) from reaction-center ITS graphs:

  • MCSMatcher — maximum common substructure mappings

  • MTG — MTG construction from ITS graphs and MCS mapping

The current MTG direction is aligned with LSG/ITS. Invariant atom data such as element and atom_map should be stored once, while temporal fields such as charge, hcount, lone_pairs, radical, sigma_order, and pi_order store compact histories across snapshots. This avoids redundant *_step_history attributes and makes MTG-to-ITS round trips easier to inspect.

MTG to ordered ITS steps#
1from synkit.Graph.MTG.mtg import MTG
2
3mtg = MTG([step_1_its, step_2_its])
4step_its = mtg.get_its_steps()
5composed = mtg.get_compose_its()

When an MTG is built from RSMI strings, SynKit 1.4.0 converts those strings to Lewis State Graph ITS by default:

RSMI sequence to LSG MTG#
1mtg = MTG(step_rsmis, mcs_mol=True)

Legacy string conversion is still available for compatibility:

Legacy MTG from RSMI strings#
1mtg = MTG(step_rsmis, mcs_mol=True, its_format="typesGH")

Compact MTG data model#

An LSG-backed MTG is a normal networkx.Graph. Node attributes split into two categories:

Attribute type

Examples

Meaning

Invariant atom fields

element, atom_map, valence_electrons

Stored once because the atom identity does not change across the mechanism.

State timelines

hcount, charge, radical, lone_pairs, present

Tuples with one value per mechanism state. For n elementary steps, these timelines have length n + 1.

Bond timelines

kekule_order, sigma_order, pi_order

Tuples with one bond state per mechanism state. None means the bond or one endpoint is outside that state; 0 means both atoms are present but no bond exists.

This compact form intentionally avoids legacy typesGH and redundant *_step_history attributes in the new Lewis State Graph path.

Example: LSG MTG changed core#

This example reads a stepwise aldol mechanism, constructs an LSG-backed MTG directly from the RSMI strings, and visualizes the changed core. The default MTG string conversion uses format="tuple" internally, so the result stores Lewis-state timelines rather than legacy typesGH fields.

Building and visualizing a compact LSG MTG#
 1from synkit.IO import load_database
 2from synkit.Graph.MTG.mtg import MTG
 3from synkit.Vis import draw_mtg_graph
 4
 5data = load_database("Data/Testcase/mech.json.gz")[0]
 6neutral = data["mechanisms"][1]
 7steps = [step["smart_string"] for step in neutral["steps"]]
 8
 9mtg = MTG(steps, mcs_mol=True)
10graph = mtg.get_mtg()
11
12assert mtg._tuple_its
13assert not any("typesGH" in attrs for _, attrs in graph.nodes(data=True))
14
15fig, ax = draw_mtg_graph(
16    mtg,
17    title=f"{neutral['mech_name']} - changed core",
18    changed_only=True,
19    show_edge_labels=True,
20    compress=True,
21)

compress=True labels only the first and final state of each changed edge. Use compress=False when debugging the full mechanism-state sequence.

Compact LSG MTG changed-core visualization

Figure: LSG MTG changed-core view for the neutral aldol mechanism. Green edges are net formed, red edges are net broken, and pink dashed edges are transient timelines that change internally but have the same compressed first/final state.

Round-trip helpers#

MTGs can be projected back to their ordered ITS steps or to a composed outer-state ITS:

MTG projections#
1step_its = mtg.get_its_steps()
2step_rsmi = mtg.get_rsmi_steps()
3composed = mtg.get_compose_its()

Use get_its_steps() when validating temporal history. Use get_compose_its() when you need the net start/end reaction encoded as a single ITS graph.

Functional Groups#

The synkit.Graph.FG package detects functional groups directly on SynKit molecular networkx graphs. It avoids an external FG representation and returns labels in graph/node-index space.

Core APIs:

Functional groups from SMILES#
1from synkit.Graph.FG import smiles_to_graph_and_functional_groups
2
3graph, groups = smiles_to_graph_and_functional_groups(
4    "CC(=O)OC1=CC=CC=C1C(=O)O"
5)
6
7print(groups)

Example output

[('ester', (2, 3, 4)), ('carboxylic_acid', (11, 12, 13))]

Detection is hierarchical: specific labels such as carboxylic_acid can suppress generic nested labels such as carbonyl when the broader label would be less useful. Public labels cover common carbonyl/acyl, oxygen, nitrogen/C=N, sulfur, boron, silicon, phosphorus, and heteroaromatic families.

Context graph#

The synkit.Graph.Context submodule expands reaction centers to include local neighborhoods, enabling context-aware matching and analysis.

Context graph expansion example#
 1from synkit.IO import rsmi_to_its
 2from synkit.Graph.Context.radius_expand import RadiusExpand
 3from synkit.Vis.graph_visualizer import GraphVisualizer
 4
 5smart = (
 6    '[CH3:1][O:2][C:3](=[O:4])[CH:5]([CH2:6][CH2:7][CH2:8][CH2:9]'
 7    '[NH:10][C:11](=[O:12])[O:13][CH2:14][c:15]1[cH:16][cH:17]'
 8    '[cH:18][cH:19][cH:20]1)[NH:21][C:22](=[O:23])[NH:24][c:25]1'
 9    '[cH:26][c:27]([O:28][CH3:29])[cH:30][c:31]([C:32]([CH3:33])'
10    '([CH3:34])[CH3:35])[c:36]1[OH:37].[OH:38][H:39]>>'
11    '[C:11](=[O:12])([O:13][CH2:14][c:15]1[cH:16][cH:17][cH:18]'
12    '[cH:19][cH:20]1)[OH:38].[CH3:1][O:2][C:3](=[O:4])[CH:5]'
13    '([CH2:6][CH2:7][CH2:8][CH2:9][NH:10][H:39])[NH:21][C:22]'
14    '(=[O:23])[NH:24][c:25]1[cH:26][c:27]([O:28][CH3:29])[cH:30]'
15    '[c:31]([C:32]([CH3:33])([CH3:34])[CH3:35])[c:36]1[OH:37]'
16)
17
18its = rsmi_to_its(smart)
19rc = rsmi_to_its(smart, core=True)
20
21exp = RadiusExpand()
22k1 = exp.extract_k(its, n_knn=1)
23
24gv = GraphVisualizer()
25gv.visualize_its_grid([rc, k1])
Context graph expansion example

Figure: (A) Minimal reaction center. (B) First-shell context expansion (k=1).

See Also#

  • synkit.IO — format conversion utilities

  • synkit.Synthesis — reaction prediction and network exploration