C++ Bindings

Grouper bindings for Python

class Grouper._Grouper.Atom

Bases: pybind11_object

Represents an atom in an AtomGraph.

from Grouper import Atom

# Create a carbon atom
carbon = Atom("C", 4)
property type

The type of the atom (e.g., ‘C’, ‘O’).

property valency

The valency of the atom.

class Grouper._Grouper.AtomGraph

Bases: pybind11_object

Represents a graph of atoms.

from Grouper import AtomGraph

# Create an empty atom graph
atom_graph = AtomGraph()
add_edge(self: Grouper._Grouper.AtomGraph, src: SupportsInt | SupportsIndex, dst: SupportsInt | SupportsIndex, order: SupportsFloat | SupportsIndex = 1.0, validate: bool = True) → None

Adds a bond to the graph.

add_node(*args, **kwargs)

Overloaded function.

  1. add_node(self: Grouper._Grouper.AtomGraph, type: str, valency: typing.SupportsInt | typing.SupportsIndex = -1) -> None

Adds an atom to the graph.

  1. add_node(self: Grouper._Grouper.AtomGraph, atom: Grouper._Grouper.Atom) -> None

Adds an atom to the graph from an Atom object.

property edges

The bonds in the graph.

free_valency(self: Grouper._Grouper.AtomGraph, arg0: SupportsInt | SupportsIndex) → int

Get the free valency of the graph.

from_smarts(self: Grouper._Grouper.AtomGraph, arg0: str) → None

Creates an AtomGraph from a SMARTS string.

from_smiles(self: Grouper._Grouper.AtomGraph, arg0: str) → None

Creates an AtomGraph from a SMILES string.

from Grouper import AtomGraph

atom_graph = AtomGraph()
atom_graph.from_smiles("CCO")
property nodes

The atoms in the graph.

substructure_search(self: Grouper._Grouper.AtomGraph, arg0: Grouper._Grouper.AtomGraph, arg1: collections.abc.Sequence[SupportsInt | SupportsIndex]) → list[list[tuple[int, int]]]

Performs a substructure search on the graph.

from Grouper import AtomGraph

graph = AtomGraph()
graph.from_smiles("CCO")
sub = AtomGraph()
sub.from_smiles("CO")
matches = graph.substructure_search(sub)
to_canonical(self: Grouper._Grouper.AtomGraph) → list[int]

Return a canonical form of the AtomGraph as a list of nauty setwords followed by the canonical color sequence. Two AtomGraphs return equal lists iff they are isomorphic with atom element types and bond orders preserved.

class Grouper._Grouper.Group

Bases: pybind11_object

Represents a chemical group with a SMARTS pattern and connection ports.

from Grouper import Group

# Create a methyl group
methyl = Group("methyl", "[CH3]", [0])
property hubs

The hub atoms of the group.

property pattern

The SMARTS pattern of the group.

property pattern_type

The type of the pattern (e.g., SMILES, SMARTS).

property ports

The connection ports of the group.

possible_attachments(self: Grouper._Grouper.Group, arg0: SupportsInt | SupportsIndex) → list[list[int]]

Get the number of possible attachments for the group.

property type

The type of the group.

class Grouper._Grouper.GroupGraph

Bases: pybind11_object

Represents a graph of chemical groups.

from Grouper import GroupGraph

# Create an empty graph
graph = GroupGraph()
add_edge(self: Grouper._Grouper.GroupGraph, src: tuple[SupportsInt | SupportsIndex, SupportsInt | SupportsIndex] = (0, 0), dst: tuple[SupportsInt | SupportsIndex, SupportsInt | SupportsIndex] = (0, 0), order: SupportsFloat | SupportsIndex = 1.0, strict: bool = True) → bool

Adds an edge to the graph.

graph = GroupGraph()
graph.add_node("methyl", "[CH3]", [0])
graph.add_node("hydroxyl", "[OH]", [0])
graph.add_edge((0, 0), (1, 0))
add_node(*args, **kwargs)

Overloaded function.

  1. add_node(self: Grouper._Grouper.GroupGraph, type: str = ‘’, pattern: str = ‘’, hubs: collections.abc.Sequence[typing.SupportsInt | typing.SupportsIndex] = [], pattern_type: str = ‘SMILES’) -> None

Adds a node to the graph.

graph = GroupGraph()
graph.add_node("methyl", "[CH3]", [0])
  1. add_node(self: Grouper._Grouper.GroupGraph, group: Grouper._Grouper.Group) -> None

Adds a node to the graph from a Group object.

clear_edges(self: Grouper._Grouper.GroupGraph) → None

Remove all edges from the graph.

compute_orbits(self: Grouper._Grouper.GroupGraph, edge_list: collections.abc.Sequence[tuple[SupportsInt | SupportsIndex, SupportsInt | SupportsIndex]], node_colors: collections.abc.Sequence[SupportsInt | SupportsIndex]) → tuple[list[int], list[int], list[list[int]]]

Compute the orbits of the graph.

graph = GroupGraph()
graph.add_node("ester", "C(O)=O", [0, 1])
graph.add_node("ester", "C(O)=O", [0, 1])
graph.add_node("ester", "C(O)=O", [0, 1])
graph.add_node("not-ester", "C", [0, 0])

edge_list = [ (0, 2),(0, 3),(1, 2),(1, 3) ]
node_colors = [0,0,0,1]
node_orbits, edge_orbits, _ = graph.compute_orbits(edge_list, node_colors)
property edges

The edges of the graph.

from_json(self: Grouper._Grouper.GroupGraph, arg0: str) → None

Deserialize a GroupGraph from a JSON string.

graph = GroupGraph()
graph.add_node("type1", "C", [0, 0])
graph.add_node("type1", "C", [0, 0])
graph.add_edge((0, 0), (1, 0))
json_str = graph.to_json()
graph2 = GroupGraph()
graph2.from_json(json_str)
property is_coarse_grained

Whether the graph is coarse-grained.

is_port_free(self: Grouper._Grouper.GroupGraph, node_id: SupportsInt | SupportsIndex, port: SupportsInt | SupportsIndex) → bool

Check if a port is free.

n_free_ports(self: Grouper._Grouper.GroupGraph, arg0: SupportsInt | SupportsIndex) → int

Get the number of free ports in the graph.

property node_types

The types of the nodes in the graph.

property nodes

The nodes of the graph.

to_3d(**kw)

Generate 3D coordinates via ETKDG + force-field minimization. Returns an RDKit Mol with explicit hydrogens. See Grouper.exports.to_3d_mol for parameters.

to_atom_graph(self: Grouper._Grouper.GroupGraph) → AtomGraph

Convert GroupGraph to AtomGraph.

graph = GroupGraph()
graph.add_node(type="carbon", pattern= "C", hubs = [0,0,0,0])
graph.add_node(type="nitrogen", pattern = "N", hubs = [0,0,0])
graph.add_edge((0, 0), (1, 0))
atom_graph = graph.to_atom_graph()
to_canonical(self: Grouper._Grouper.GroupGraph) → list[int]

Return a canonical form of the GroupGraph as a list of nauty setwords followed by the canonical color sequence.

Two graphs return equal lists iff they are isomorphic with group ntype, port hub label, and bond order preserved. The canonical form is what exhaustive_generate’s dedup uses internally and is suitable as a hash key for cross-run deduplication.

from Grouper import GroupGraph

g1 = GroupGraph()
g1.add_node("methyl", "C", [0])
g1.add_node("hydroxyl", "O", [0])
g1.add_edge((0, 0), (1, 0))

# Same molecule, nodes added in the reverse order:
g2 = GroupGraph()
g2.add_node("hydroxyl", "O", [0])
g2.add_node("methyl", "C", [0])
g2.add_edge((1, 0), (0, 0))

assert g1.to_canonical() == g2.to_canonical()
to_inchi()

Return the IUPAC InChI string (cross-tool deterministic identifier).

to_inchi_key()

Return the 27-character InChIKey — primary-key form used by most chemistry databases (PubChem, ChEMBL, NIST).

to_json(self: Grouper._Grouper.GroupGraph) → str

Serialize the GroupGraph to a JSON string.

graph = GroupGraph()
graph.add_node("type1", "C", [0, 0])
graph.add_node("type1", "C", [0, 0])
graph.add_edge((0, 0), (1, 0))
json_str = graph.to_json()
to_mol(path=None, **kw)

Render as a V2000 MOL block (single-molecule).

to_pdb(path=None, **kw)

Render as PDB (always 3D).

to_sdf(path=None, **kw)

Render as SDF. Pass path to write to disk; otherwise returns the SDF as a string. See Grouper.exports.to_sdf for parameters.

to_smarts()

Return the SMARTS pattern for this molecule. Useful as a query pattern in substructure searches downstream.

to_smiles(self: Grouper._Grouper.GroupGraph) → str

Convert GroupGraph to SMILES string.

graph = GroupGraph()
graph.add_node("methyl", "[CH3]", [0])
graph.add_node("hydroxyl", "[OH]", [0])
graph.add_edge((0, 0), (1, 0))
# print(graph.to_smiles()) # Output: CO
to_vector(self: Grouper._Grouper.GroupGraph) → dict[str, int]

Convert GroupGraph to group vector.

graph = GroupGraph()
graph.add_node("methyl", "[CH3]", [0])
graph.add_node("hydroxyl", "[OH]", [0])
graph.add_edge((0, 0), (1, 0))
# print(graph.to_vector()) # Output: {'methyl': 1, 'hydroxyl': 1}
to_xyz(path=None, **kw)

Render as XYZ (always 3D).

visualize(figsize=(10, 5), structure_size=(400, 400))

Render a side-by-side view of the molecule for this GroupGraph.

Two panels:

Left — 2D chemical structure (atoms and bonds), via RDKit’s

Draw.MolToImage on the SMILES of this graph.

Right — port-graph (groups as colored nodes, ports as edges),

via Grouper.visualization.visualize_graph.visualize.

The two views answer different questions: the left tells you what molecule this is in chemistry terms; the right tells you how Grouper sees it — which groups, which ports, which edges. Together they’re the easiest way to sanity-check a generated structure in a notebook.

Returns the matplotlib Figure so callers can plt.show(), fig.savefig(…), or embed it.

exception Grouper._Grouper.GrouperFragmentationError

Bases: Exception

exception Grouper._Grouper.GrouperNotYetImplementedException

Bases: Exception

exception Grouper._Grouper.GrouperParseException

Bases: Exception

Grouper._Grouper.exhaustive_fragment(smiles: str, node_defs: collections.abc.Set[Grouper._Grouper.Group]) → list[Grouper._Grouper.GroupGraph]

Fragment a molecule into a set of GroupGraphs based on a given set of group definitions.

from Grouper import Group, exhaustive_fragment

node_defs = set()
node_defs.add(Group("hydroxyl", "O", [0]))
node_defs.add(Group("alkene", "C=C", [0, 0, 1, 1]))

smiles = "C=CO"

fragmented_graphs = exhaustive_fragment(smiles, node_defs)
Grouper._Grouper.exhaustive_generate(n_nodes: SupportsInt | SupportsIndex, node_defs: collections.abc.Set[Grouper._Grouper.Group], num_procs: SupportsInt | SupportsIndex = -1, vcolg_output_file: str = '', positive_constraints: collections.abc.Mapping[str, SupportsInt | SupportsIndex] = {}, negative_constraints: collections.abc.Set[str] = set(), config_path: str = '') → set

Exhaustively generate all possible GroupGraphs with a given number of nodes and a set of allowed groups.

n_nodesint

Number of group nodes per generated graph.

node_defsset[Group]

The palette of groups the enumeration is allowed to use.

num_procsint, default -1

OpenMP worker count for the colored-graph processing stage. -1 binds to all available cores.

vcolg_output_filestr, default “”

Path to a pre-computed vcolg -T output file. When empty (the default), the call pipes geng -c n | vcolg -T -m|node_defs| internally. Set this to feed in your own filtered or externally-generated colored-graph list (e.g. when sweeping a custom subset of colorings, or when reusing a large vcolg run across multiple invocations).

positive_constraintsdict[str, int], default {}

Map from group type to a minimum required count. Graphs that don’t meet every floor are dropped.

negative_constraintsset[str], default set()

SMILES substrings that disqualify a generated graph.

config_pathstr, default “”

Optional path to a key=value config file with PostgreSQL connection settings (table_name, dbname, user, password, hostaddr, port). When set, generated graphs are also persisted to the named table.

from Grouper import Group, exhaustive_generate

node_defs = [
    {"type": "t2", "smarts": "N", "hubs": [0, 0, 0]},
    {"type": "Methyl", "smarts": "C", "hubs": [0, 0, 0]},
    {"type": "ester", "smarts": "C(=O)O", "hubs": [0, 2]},
    {"type": "extra1", "smarts": "O", "hubs": [0, 0]},
]
node_defs = set(
    Group(n["type"], n["smarts"], n["hubs"], pattern_type="SMILES")
    for n in node_defs
)

generated_graphs = exhaustive_generate(2, node_defs)
Grouper._Grouper.random_generate(n_nodes: SupportsInt | SupportsIndex, node_defs: collections.abc.Set[Grouper._Grouper.Group], num_graphs: SupportsInt | SupportsIndex = 100, num_procs: SupportsInt | SupportsIndex = -1, positive_constraints: collections.abc.Mapping[str, SupportsInt | SupportsIndex] = {}, negative_constraints: collections.abc.Set[str] = set()) → set

Randomly generate a specified number of GroupGraphs.

from Grouper import Group, random_generate

node_defs = [
    {"type": "t2", "smarts": "N", "hubs": [0, 0, 0]},
    {"type": "Methyl", "smarts": "C", "hubs": [0, 0, 0]},
    {"type": "ester", "smarts": "C(=O)(O)", "hubs": [0, 2]},
    {"type": "extra1", "smarts": "O", "hubs": [0, 0]},
]
node_defs = set(
    Group(n["type"], n["smarts"], n["hubs"], pattern_type="SMILES")
    for n in node_defs
)

generated_graphs = random_generate(2, node_defs, 5)