diff --git a/.gitignore b/.gitignore
index 9eb9e68..f3e87b5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
.idea
.venv
-tests/test-results/
\ No newline at end of file
+tests/test-results/
+docs/_build/
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
new file mode 100644
index 0000000..a0d9fb0
--- /dev/null
+++ b/.readthedocs.yaml
@@ -0,0 +1,28 @@
+# .readthedocs.yaml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Set the OS, Python version and other tools you might need
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3.11"
+
+# Build documentation in the docs/ directory with Sphinx
+sphinx:
+ configuration: docs/conf.py
+
+# Optionally build your docs in additional formats such as PDF and ePub
+formats:
+ - pdf
+ - epub
+
+# Optionally declare the Python requirements required to build your docs
+python:
+ install:
+ - method: pip
+ path: .
+ - requirements: docs/requirements.txt
diff --git a/README.md b/README.md
index 803b65d..35339bc 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# ChemGED
-ChemGED is a Python package for enabling the appoximate graph edit distance (GED) computation between
-chemicals. Normally, GED is a NP-hard problem, but ChemGED uses heuristics to approximate the GED in
+ChemGED is a Python package for enabling the appoximate graph edit distance (GED) computation between
+chemicals. Normally, GED is a NP-hard problem, but ChemGED uses heuristics to approximate the GED in
a reasonable time.
## Installation
@@ -12,8 +12,8 @@ pip install chemged
```
## Usage
-To use ChemGED, you just create an ``ApproximateChemicalGED`` object, and then call the
-``compute_ged`` method with two chemical structures. They can be SMILES *or*
+To use ChemGED, you just create an ``ApproximateChemicalGED`` object, and then call the
+``compute_ged`` method with two chemical structures. They can be SMILES *or*
[RDKit](https://www.rdkit.org/docs/index.html) Mol objects.
```python
@@ -33,25 +33,36 @@ approx_ged = ged_calc.compute_ged(MolFromSmiles(chemical1), MolFromSmiles(chemic
ChemGED also implements ``pdist`` and ``cdist`` functions to compute pairwise distances between
sets of chemicals. These will return as Numpy arrays.
-> [!NOTE]
+> [!NOTE]
> ``pdist`` will return the vector-form distance vector, while ``cdist`` will return a
> square-form distance matrix. ``scipy.spatial.distance.squareform`` can be used to convert
> the vector-form distance vector to a square-form distance matrix.
```python
+from chemged import pdist, cdist
+# Create a list of chemicals
+chemicals = ["CCO", "CCN", "CC", "C"]
+
+# Compute pairwise distances
+distances_vector = pdist(chemicals)
+print(distances_vector) # Vector form
+
+# Compute all-vs-all distances
+distances_matrix = cdist(chemicals, chemicals)
+print(distances_matrix) # Square form
```
+## Documentation
+For more detailed documentation, please visit [ReadTheDocs](https://chemged.readthedocs.io/).
## Implementation
-
-The approach used here uses bipartite graph matching[[1]](#1), and most of its implementation in python
+The approach used here uses bipartite graph matching[[1]](#1), and most of its implementation in python
is based off scripts from https://github.com/priba/aproximated_ged/tree/master.
ChemGED uses [RDKit](https://www.rdkit.org/docs/index.html) to handle chemicals inside python.
-
## References
-[1]
-Riesen, Kaspar, and Horst Bunke.
-"Approximate graph edit distance computation by means of bipartite graph matching."
-Image and Vision computing 27.7 (2009): 950-959
\ No newline at end of file
+[1]
+Riesen, Kaspar, and Horst Bunke.
+"Approximate graph edit distance computation by means of bipartite graph matching."
+Image and Vision computing 27.7 (2009): 950-959
diff --git a/docs/api.rst b/docs/api.rst
new file mode 100644
index 0000000..8402288
--- /dev/null
+++ b/docs/api.rst
@@ -0,0 +1,45 @@
+=============
+API Reference
+=============
+
+This page provides detailed documentation for all the classes and functions in the ChemGED package.
+
+ApproximateChemicalGED
+----------------------
+
+.. autoclass:: src.chemged.ApproximateChemicalGED
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+ChemicalGEDCostMatrix
+---------------------
+
+.. autoclass:: src.chemged.ChemicalGEDCostMatrix
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+UniformElementCostMatrix
+------------------------
+
+.. autoclass:: src.chemged.UniformElementCostMatrix
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Chemical Utilities
+==================
+
+.. automodule:: src.chemged.chem_utils
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Plotting Utilities
+==================
+
+.. automodule:: src.chemged.plotting
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/chemical_graph_attributes.rst b/docs/chemical_graph_attributes.rst
new file mode 100644
index 0000000..e1b41ba
--- /dev/null
+++ b/docs/chemical_graph_attributes.rst
@@ -0,0 +1,26 @@
+.. _chem-graph-attributes:
+
+=========================
+Chemical Graph Attributes
+=========================
+
+ChemGED uses the NetworkX library to represent chemical graphs. Each node in the graph represents an atom,
+and each edge represents a bond between atoms. The attributes of these nodes and edges are crucial for
+calculating the graph edit distance (GED) accurately.
+
+By default, the ``mol_to_nx`` function in ChemGED will convert a chemical into a NetworkX graph with the following
+atom attributes stored in the node:
+
+- **atomic_number**: The atomic number of the atom (e.g., 6 for Carbon, 8 for Oxygen).
+- **degree**: The degree of the atom in the graph (number of bonds).
+- **formal_charge**: The formal charge of the atom.
+- **hybridization**: The hybridization state of the atom (e.g., 'sp3', 'sp2').
+- **is_aromatic**: A boolean indicating if the atom is part of an aromatic ring.
+- **is_in_ring**: A boolean indicating if the atom is part of a ring structure.
+- **hydrogen**: The number of hydrogen atoms attached to the atom.
+
+Chemical bond attributes are stored in the graph. The edge attributes include:
+- **bond_type**: The type of bond (e.g., 'single', 'double', 'triple', 'aromatic').
+
+While these are the current attributes stored in the graph, you can extend or modify these attributes as
+needed for your specific use case.
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..0c23a00
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,63 @@
+"""Configuration file for the Sphinx documentation builder."""
+
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+import os
+import sys
+
+
+sys.path.insert(0, os.path.abspath(".."))
+sys.path.insert(0, os.path.abspath("../src"))
+
+from chemged import __version__ as version
+
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+source_suffix = ".rst"
+master_doc = "index"
+
+project = "ChemGED"
+copyright = "2025, James Wellnitz"
+author = "James Wellnitz"
+release = version # Added for ReadTheDocs compatibility
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = [
+ "sphinx.ext.autodoc",
+ "sphinx.ext.viewcode",
+ "sphinx.ext.napoleon",
+ "sphinx.ext.intersphinx",
+ "sphinx.ext.mathjax",
+]
+
+templates_path = ["_templates"]
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = "sphinx_rtd_theme"
+html_static_path = ["_static"]
+
+# -- Extension configuration -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#configuration
+
+autodoc_member_order = "bysource"
+autodoc_typehints = "description"
+
+# -- Intersphinx configuration -----------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html#configuration
+
+intersphinx_mapping = {
+ "python": ("https://docs.python.org/3", None),
+ "numpy": ("https://numpy.org/doc/stable/", None),
+ "networkx": ("https://networkx.org/documentation/stable/", None),
+}
+
+issues_uri = "https://github.com/molecularmodelinglab/ChemGED/{issue}"
+issues_pr_uri = "https://github.com/molecularmodelinglab/ChemGED/pull/{pr}"
diff --git a/docs/contributing.rst b/docs/contributing.rst
new file mode 100644
index 0000000..5094065
--- /dev/null
+++ b/docs/contributing.rst
@@ -0,0 +1,213 @@
+=======================
+Contributing Guidelines
+=======================
+
+ChemGED was a side project that came about to support separate research on semantically aware
+representation learning for chemical graphs. As a result, we only ever implemented a single cost matrix
+for the chemicals, and only a single approximate GED algorithm. While it is unlikely that we will
+ever add new matrices or algorithms, we are open to contributions that would do so. If interested
+make sure to read through the documentation to understand how to
+:ref:`construct new cost matrices `.
+
+Setting Up Development Environment
+==================================
+
+1. **Fork the repository**
+
+ Start by forking the ChemGED repository on GitHub.
+
+2. **Clone your fork**
+
+ .. code-block:: bash
+
+ git clone https://github.com/YOUR_USERNAME/ChemGED.git
+ cd ChemGED
+
+3. **Set up a virtual environment**
+
+ We recommend using Poetry for dependency management:
+
+ .. code-block:: bash
+
+ # Install Poetry if you don't have it
+ pip install poetry
+
+ # Create a virtual environment and install dependencies
+ poetry install --with dev,test,docs
+
+ Alternatively, you can use pip:
+
+ .. code-block:: bash
+
+ # Create a virtual environment
+ python -m venv venv
+ source venv/bin/activate # On Windows: venv\Scripts\activate
+
+ # Install dependencies
+ pip install -e ".[dev,test,docs]"
+
+Development Workflow
+====================
+
+1. **Create a new branch**
+
+ Always create a new branch for your changes:
+
+ .. code-block:: bash
+
+ git checkout -b feature/your-feature-name
+
+2. **Make your changes**
+
+ Implement your changes, following the coding standards described below.
+
+3. **Run tests**
+
+ Make sure all tests pass:
+
+ .. code-block:: bash
+
+ pytest
+
+ To run tests with coverage:
+
+ .. code-block:: bash
+
+ pytest --cov=chemged
+
+4. **Update documentation**
+
+ If you've added or modified functionality, update the documentation accordingly.
+
+5. **Commit your changes**
+
+ Follow the commit message guidelines below.
+
+6. **Push your changes**
+
+ .. code-block:: bash
+
+ git push origin feature/your-feature-name
+
+7. **Create a pull request**
+
+ Go to the GitHub repository and create a pull request from your branch to the main branch.
+
+Coding Standards
+================
+
+We follow standard Python coding conventions:
+
+1. **PEP 8**
+
+ Follow the PEP 8 style guide for Python code. You can use tools like flake8 or black to help with this.
+
+2. **Type Hints**
+
+ Use type hints for function and method signatures:
+
+ .. code-block:: python
+
+ def example_function(param1: str, param2: int) -> bool:
+ # ...
+
+3. **Docstrings**
+
+ Use NumPy-style docstrings for all functions, classes, and methods:
+
+ .. code-block:: python
+
+ def example_function(param1, param2):
+ """
+ Brief description of the function.
+
+ Parameters
+ ----------
+ param1 : type
+ Description of param1
+ param2 : type
+ Description of param2
+
+ Returns
+ -------
+ type
+ Description of return value
+ """
+ # ...
+
+4. **Tests**
+
+ Write tests for all new functionality using pytest.
+ It is best to make a test case for each new function to maintain high coverage.
+
+ChemGED provides a ``pre-commit`` configuration to help with coding standard checks.
+These checks will be enforced prior to merging a pull request, so it is best to check them
+before you make a pull results.
+
+To set up pre-commit hooks, run:
+
+.. code-block:: bash
+
+ pre-commit install
+
+This will install the pre-commit hooks defined in the `.pre-commit-config.yaml` file.
+
+
+Commit Message Guidelines
+=========================
+
+We follow the conventional commits specification:
+
+- **feat**: A new feature
+- **fix**: A bug fix
+- **docs**: Documentation only changes
+- **style**: Changes that do not affect the meaning of the code (white-space, formatting, etc)
+- **refactor**: A code change that neither fixes a bug nor adds a feature
+- **perf**: A code change that improves performance
+- **test**: Adding missing tests or correcting existing tests
+- **chore**: Changes to the build process or auxiliary tools
+
+Example:
+
+.. code-block:: text
+
+ feat(cost): add new atomic property cost matrix
+
+ This commit adds a new cost matrix implementation that uses atomic properties
+ to determine costs for GED calculation.
+
+Pull Request Process
+====================
+
+1. Ensure all tests pass.
+2. Update the documentation if necessary.
+3. The PR should be reviewed by at least one maintainer.
+4. Once approved, a maintainer will merge the PR.
+
+Reporting Bugs
+==============
+
+If you find a bug, please report it by creating an issue on GitHub. Include the following information:
+
+1. A clear description of the bug
+2. Steps to reproduce the bug
+3. Expected behavior
+4. Actual behavior
+5. Environment information (Python version, OS, etc.)
+6. If possible, a minimal code example that reproduces the bug
+
+Feature Requests
+================
+
+If you have an idea for a new feature, please create an issue on GitHub with the following information:
+
+1. A clear description of the feature
+2. The motivation for the feature
+3. If possible, a sketch of how the feature might be implemented
+
+
+Questions?
+==========
+
+If you have any questions about contributing, feel free to open an issue on GitHub or reach
+out to the maintainers directly.
diff --git a/docs/custom_cost_matrix.rst b/docs/custom_cost_matrix.rst
new file mode 100644
index 0000000..7873df5
--- /dev/null
+++ b/docs/custom_cost_matrix.rst
@@ -0,0 +1,170 @@
+.. _custom_cost_matrix:
+
+====================
+Custom Cost Matrices
+====================
+
+The cost matrix is a crucial component of the Approximate Graph Edit Distance (GED) calculation used in ChemGED.
+
+This matrix takes form:
+
+.. math::
+
+ \\mathbf{C} =
+ \\begin{bmatrix}
+ c_{1,1} & c_{1,2} & \\cdots & c_{1,m} & c_{1,\\epsilon} & \\infty & \\cdots & \\infty \\\\
+ c_{2,1} & c_{2,2} & \\cdots & c_{2,m} & \\infty & c_{2,\\epsilon} & \\ddots & \\vdots \\\\
+ \\vdots & \\vdots & \\ddots & \\vdots & \\vdots & \\ddots & \\ddots & \\infty \\\\
+ c_{n,1} & c_{n,2} & \\cdots & c_{n,m} & \\infty & \\cdots & \\infty & c_{n,\\epsilon}\\\\
+ c_{\\epsilon, 1} & \\infty & \\cdots & \\infty & 0 & 0 & \\cdots & 0 \\\\
+ \\infty & c_{\\epsilon, 2} & \\ddots & \\vdots & 0 & 0 & \\ddots & \\vdots \\\\
+ \\vdots & \\ddots & \\ddots & \\infty & \\vdots & \\ddots & \\ddots & 0 \\\\
+ \\infty & \\cdots & \\infty & c_{\\epsilon, m} & 0 & \\cdots & 0 & 0 \\\\
+ \\end{bmatrix}
+
+where :math:`c_{n,m}` is the cost of substituting node :math:`n` in graph :math:`n1`
+with node :math:`m` in graph :math:`n2`, :math:`c_{n,\\epsilon}` is the cost of deleting
+a node from :math:`n1`, and :math:`c_{\\epsilon,m}` is the cost of inserting node
+:math:`m` from :math:`n2` into :math:`n1`. Note that in this cost matrix form, the
+cost of substituting a node takes into account the cost of the edge operations as well.
+The edge cost is also defined using the same matrix structure, except for the edge
+between two nodes. A more detailed explanation of the cost matrix can be found in the
+paper by [1]_.
+
+This is alot to handle, so ChemGED provides a abstract class ``ChemicalGEDCostMatrix``
+that defines the interface for cost matrices definition. Rather than defining how to make
+the full cost matrix in one go. Instead you need to define the follow cost functions:
+
+- Node substitution: Generates a cost matrix for replacing a node in one graph with a node in another graph
+ (shape: Num Nodes in Graph 1 x Num Nodes in Graph 2)
+- Node insertion: Generates a cost vector for inserting a node from from the graph (shape: Num Nodes in Graph)
+- Node deletion: Generates a cost vector for deleting a node from from the graph (shape: Num Nodes in Graph)
+- Edge substitution: Generates a cost matrix for replacing all the edges in one node with edges in another node
+ (shape: Num Edges in node 1 x Num Edges in node 2)
+- Edge insertion: Generates a cost vector for inserting a edge from from the node (shape: Num edges in Graph)
+- Edge deletion: Generates a cost vector for deleting a edge from from the graph (shape: Num edge in Graph)
+
+With these functions defined, ChemGED will take care of the rest to construct the full cost matrix and run the
+approximate GED algorithm.
+
+By default, ChemGED will use the ``UniformElementCostMatrix`` which defines a uniform cost for all operations.
+This is a good starting point, but you allows limited control over atomic properties and bond types. Swapping
+a Cl with a Br will have a bigger impact of the chemical than swapping a C with a N, for example. If this is
+something you want to take into account, you can create your own cost matrix by subclassing
+``ChemicalGEDCostMatrix`` and implementing the required methods taking into account the chemical properties of
+the nodes and edges.
+
+Creating a Custom Cost Matrix
+=============================
+
+To create a custom cost matrix, you need to subclass ``ChemicalGEDCostMatrix`` and implement the required methods:
+
+.. code-block:: python
+
+ from chemged import ChemicalGEDCostMatrix
+ import networkx as nx
+ import numpy as np
+
+ class MyCustomCostMatrix(ChemicalGEDCostMatrix):
+ def __init__(self, custom_param1, custom_param2):
+ # Initialize your custom parameters if needed
+ self.custom_param1 = custom_param1
+ self.custom_param2 = custom_param2
+
+ def get_node_substitution_costs(self, g1: nx.Graph, g2: nx.Graph) -> np.ndarray:
+ # Implement your custom node substitution cost logic
+ # Return a matrix of shape (len(g1.nodes), len(g2.nodes))
+ # where each element (i, j) is the cost of substituting node i in g1 with node j in g2
+ pass
+
+ def get_node_insertion_costs(self, g: nx.Graph) -> np.ndarray:
+ # Implement your custom node insertion cost logic
+ # Return a vector of shape (len(g.nodes),)
+ # where each element i is the cost of inserting node i
+ pass
+
+ def get_node_deletion_costs(self, g: nx.Graph) -> np.ndarray:
+ # Implement your custom node deletion cost logic
+ # Return a vector of shape (len(g.nodes),)
+ # where each element i is the cost of deleting node i
+ pass
+
+ def get_edge_substitution_costs(self, n1: nx.classes.coreviews.AtlasView, n2: nx.classes.coreviews.AtlasView) -> np.ndarray:
+ # Implement your custom edge substitution cost logic
+ # Return a matrix of shape (len(n1), len(n2))
+ # where each element (i, j) is the cost of substituting edge i in n1 with edge j in n2
+ pass
+
+ def get_edge_insertion_costs(self, n: nx.classes.coreviews.AtlasView) -> np.ndarray:
+ # Implement your custom edge insertion cost logic
+ # Return a vector of shape (len(n),)
+ # where each element i is the cost of inserting edge i
+ pass
+
+ def get_edge_deletion_costs(self, n: nx.classes.coreviews.AtlasView) -> np.ndarray:
+ # Implement your custom edge deletion cost logic
+ # Return a vector of shape (len(n),)
+ # where each element i is the cost of deleting edge i
+ pass
+
+Example: Custom Cost Matrix to Ignore Halogen Swaps
+===================================================
+
+Here's an example of how to define the a custom cost matrix that modifies the
+UniformElementCostMatrix to ignore swaps between halogens (F, Cl, Br, I):
+
+.. code-block:: python
+
+ from chemged import UniformElementCostMatrix
+ import networkx as nx
+
+ class HalogenSwapCostMatrix(UniformElementCostMatrix):
+ def get_node_substitution_costs(self, g1: nx.Graph, g2: nx.Graph) -> np.ndarray:
+ v1 = list(nx.get_node_attributes(g1, "atomic_num").values())
+ v2 = list(nx.get_node_attributes(g2, "atomic_num").values())
+
+ v1[np.where(np.isin(v1, [9, 17, 35, 53]))] = -1 # alias for F, Cl, Br, I
+ v2[np.where(np.isin(v2, [9, 17, 35, 53]))] = -1 # alias for F, Cl, Br, I
+
+ # generate the cost matrix
+ return ~np.equal.outer(v1, v2) * self.node_sub_cost
+
+Now the chemicals "CCCCCCl" and "CCCCCBr" will have a GED of 0.
+This can be a very helpful tool if trying to build more chemically aware
+similarity calculations.
+
+.. note::
+ atoms in the networkx graph have a handful of attributes other than "atomic_num", see the
+ :ref:`chemical graph attributes ` for more information
+ on what you can use (or how to add new ones).
+
+Using Your Custom Cost Matrix
+=============================
+
+Once you've implemented your custom cost matrix, you can use it with the ApproximateChemicalGED class:
+
+.. code-block:: python
+
+ from chemged import ApproximateChemicalGED
+ from my_module import HalogenSwapCostMatrix
+
+ # Create an instance of your custom cost matrix
+ custom_cost_matrix = HalogenSwapCostMatrix()
+
+ # Create an instance of ApproximateChemicalGED with your custom cost matrix
+ ged_calc = ApproximateChemicalGED(cost_matrix=custom_cost_matrix)
+
+ # Compute the approximate GED
+ ged = ged_calc.compute_ged("CCCCCCl", "CCCCCBr")
+ print(f"Approximate GED: {ged}")
+ >>> Approximate GED: 0.0
+
+Tips for Implementing Custom Cost Matrices
+==========================================
+While the approximate GED algorithm is far faster than a brute for GED calculation, it still can be computationally expensive,
+especially if generating the cost matrix is not efficient. Try to limit the number of operations in the cost matrix generation
+functions to only what is necessary for you case, and keep it as effiecent as you can.
+
+References
+==========
+.. [1] Riesen, Kaspar, and Horst Bunke. "Approximate graph edit distance computation by means of bipartite graph matching." Image and Vision computing 27.7 (2009): 950-959
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..1b55c12
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,36 @@
+=======
+ChemGED
+=======
+
+ChemGED is a Python package for enabling the appoximate graph edit distance (GED) computation between
+chemicals. Normally, GED is a NP-hard problem, but ChemGED uses heuristics to approximate the GED in
+a reasonable time.
+
+.. code-block:: python
+
+ from chemged import ApproximateChemicalGED, UniformElementCostMatrix
+
+ ged_calc = ApproximateChemicalGED(cost_matrix=UniformElementCostMatrix())
+
+ # Compute the approximate GED
+ ged = ged_calc.compute_ged("CC(C)Cc1ccc(cc1)[C@@H](C)C(=O)O", "CC(=O)Nc1ccc(O)cc1")
+ print(f"Approximate GED: {ged}")
+ >>> Approximate GED: 12.0
+
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contents:
+
+ installation
+ chemical_graph_attributes
+ custom_cost_matrix
+ api
+ contributing
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/installation.rst b/docs/installation.rst
new file mode 100644
index 0000000..1784cb9
--- /dev/null
+++ b/docs/installation.rst
@@ -0,0 +1,20 @@
+============
+Installation
+============
+
+ChemGED can be installed using pip.
+
+.. code-block:: bash
+
+ pip install chemged
+
+From source
+-----------
+
+You can also install ChemGED from source:
+
+.. code-block:: bash
+
+ git clone https://github.com/molecularmodelinglab/ChemGED.git
+ cd ChemGED
+ pip install -e .
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..3c934c0
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,6 @@
+sphinx>=7.1.2
+sphinx_rtd_theme>=1.3.0
+networkx>=3.5
+numpy>=2.0.0
+scipy>=1.15.3
+rdkit>=2024.9.5
diff --git a/poetry.lock b/poetry.lock
index 60987e5..61a69f6 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -640,14 +640,14 @@ files = [
[[package]]
name = "docutils"
-version = "0.21.2"
+version = "0.18.1"
description = "Docutils -- Python Documentation Utilities"
optional = false
-python-versions = ">=3.9"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
groups = ["docs"]
files = [
- {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"},
- {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"},
+ {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"},
+ {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"},
]
[[package]]
@@ -1507,63 +1507,63 @@ files = [
[[package]]
name = "numpy"
-version = "2.3.0"
+version = "2.3.1"
description = "Fundamental package for array computing in Python"
optional = false
python-versions = ">=3.11"
groups = ["main", "test"]
files = [
- {file = "numpy-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3c9fdde0fa18afa1099d6257eb82890ea4f3102847e692193b54e00312a9ae9"},
- {file = "numpy-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46d16f72c2192da7b83984aa5455baee640e33a9f1e61e656f29adf55e406c2b"},
- {file = "numpy-2.3.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a0be278be9307c4ab06b788f2a077f05e180aea817b3e41cebbd5aaf7bd85ed3"},
- {file = "numpy-2.3.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:99224862d1412d2562248d4710126355d3a8db7672170a39d6909ac47687a8a4"},
- {file = "numpy-2.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2393a914db64b0ead0ab80c962e42d09d5f385802006a6c87835acb1f58adb96"},
- {file = "numpy-2.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:7729c8008d55e80784bd113787ce876ca117185c579c0d626f59b87d433ea779"},
- {file = "numpy-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:06d4fb37a8d383b769281714897420c5cc3545c79dc427df57fc9b852ee0bf58"},
- {file = "numpy-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c39ec392b5db5088259c68250e342612db82dc80ce044cf16496cf14cf6bc6f8"},
- {file = "numpy-2.3.0-cp311-cp311-win32.whl", hash = "sha256:ee9d3ee70d62827bc91f3ea5eee33153212c41f639918550ac0475e3588da59f"},
- {file = "numpy-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:43c55b6a860b0eb44d42341438b03513cf3879cb3617afb749ad49307e164edd"},
- {file = "numpy-2.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:2e6a1409eee0cb0316cb64640a49a49ca44deb1a537e6b1121dc7c458a1299a8"},
- {file = "numpy-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:389b85335838155a9076e9ad7f8fdba0827496ec2d2dc32ce69ce7898bde03ba"},
- {file = "numpy-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9498f60cd6bb8238d8eaf468a3d5bb031d34cd12556af53510f05fcf581c1b7e"},
- {file = "numpy-2.3.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:622a65d40d8eb427d8e722fd410ac3ad4958002f109230bc714fa551044ebae2"},
- {file = "numpy-2.3.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b9446d9d8505aadadb686d51d838f2b6688c9e85636a0c3abaeb55ed54756459"},
- {file = "numpy-2.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:50080245365d75137a2bf46151e975de63146ae6d79f7e6bd5c0e85c9931d06a"},
- {file = "numpy-2.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c24bb4113c66936eeaa0dc1e47c74770453d34f46ee07ae4efd853a2ed1ad10a"},
- {file = "numpy-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4d8d294287fdf685281e671886c6dcdf0291a7c19db3e5cb4178d07ccf6ecc67"},
- {file = "numpy-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6295f81f093b7f5769d1728a6bd8bf7466de2adfa771ede944ce6711382b89dc"},
- {file = "numpy-2.3.0-cp312-cp312-win32.whl", hash = "sha256:e6648078bdd974ef5d15cecc31b0c410e2e24178a6e10bf511e0557eed0f2570"},
- {file = "numpy-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:0898c67a58cdaaf29994bc0e2c65230fd4de0ac40afaf1584ed0b02cd74c6fdd"},
- {file = "numpy-2.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:bd8df082b6c4695753ad6193018c05aac465d634834dca47a3ae06d4bb22d9ea"},
- {file = "numpy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5754ab5595bfa2c2387d241296e0381c21f44a4b90a776c3c1d39eede13a746a"},
- {file = "numpy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d11fa02f77752d8099573d64e5fe33de3229b6632036ec08f7080f46b6649959"},
- {file = "numpy-2.3.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:aba48d17e87688a765ab1cd557882052f238e2f36545dfa8e29e6a91aef77afe"},
- {file = "numpy-2.3.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4dc58865623023b63b10d52f18abaac3729346a7a46a778381e0e3af4b7f3beb"},
- {file = "numpy-2.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:df470d376f54e052c76517393fa443758fefcdd634645bc9c1f84eafc67087f0"},
- {file = "numpy-2.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:87717eb24d4a8a64683b7a4e91ace04e2f5c7c77872f823f02a94feee186168f"},
- {file = "numpy-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fa264d56882b59dcb5ea4d6ab6f31d0c58a57b41aec605848b6eb2ef4a43e8"},
- {file = "numpy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e651756066a0eaf900916497e20e02fe1ae544187cb0fe88de981671ee7f6270"},
- {file = "numpy-2.3.0-cp313-cp313-win32.whl", hash = "sha256:e43c3cce3b6ae5f94696669ff2a6eafd9a6b9332008bafa4117af70f4b88be6f"},
- {file = "numpy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:81ae0bf2564cf475f94be4a27ef7bcf8af0c3e28da46770fc904da9abd5279b5"},
- {file = "numpy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:c8738baa52505fa6e82778580b23f945e3578412554d937093eac9205e845e6e"},
- {file = "numpy-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:39b27d8b38942a647f048b675f134dd5a567f95bfff481f9109ec308515c51d8"},
- {file = "numpy-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0eba4a1ea88f9a6f30f56fdafdeb8da3774349eacddab9581a21234b8535d3d3"},
- {file = "numpy-2.3.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0f1f11d0a1da54927436505a5a7670b154eac27f5672afc389661013dfe3d4f"},
- {file = "numpy-2.3.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:690d0a5b60a47e1f9dcec7b77750a4854c0d690e9058b7bef3106e3ae9117808"},
- {file = "numpy-2.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8b51ead2b258284458e570942137155978583e407babc22e3d0ed7af33ce06f8"},
- {file = "numpy-2.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:aaf81c7b82c73bd9b45e79cfb9476cb9c29e937494bfe9092c26aece812818ad"},
- {file = "numpy-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f420033a20b4f6a2a11f585f93c843ac40686a7c3fa514060a97d9de93e5e72b"},
- {file = "numpy-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d344ca32ab482bcf8735d8f95091ad081f97120546f3d250240868430ce52555"},
- {file = "numpy-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:48a2e8eaf76364c32a1feaa60d6925eaf32ed7a040183b807e02674305beef61"},
- {file = "numpy-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ba17f93a94e503551f154de210e4d50c5e3ee20f7e7a1b5f6ce3f22d419b93bb"},
- {file = "numpy-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f14e016d9409680959691c109be98c436c6249eaf7f118b424679793607b5944"},
- {file = "numpy-2.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80b46117c7359de8167cc00a2c7d823bdd505e8c7727ae0871025a86d668283b"},
- {file = "numpy-2.3.0-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:5814a0f43e70c061f47abd5857d120179609ddc32a613138cbb6c4e9e2dbdda5"},
- {file = "numpy-2.3.0-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:ef6c1e88fd6b81ac6d215ed71dc8cd027e54d4bf1d2682d362449097156267a2"},
- {file = "numpy-2.3.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33a5a12a45bb82d9997e2c0b12adae97507ad7c347546190a18ff14c28bbca12"},
- {file = "numpy-2.3.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:54dfc8681c1906d239e95ab1508d0a533c4a9505e52ee2d71a5472b04437ef97"},
- {file = "numpy-2.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e017a8a251ff4d18d71f139e28bdc7c31edba7a507f72b1414ed902cbe48c74d"},
- {file = "numpy-2.3.0.tar.gz", hash = "sha256:581f87f9e9e9db2cba2141400e160e9dd644ee248788d6f90636eeb8fd9260a6"},
+ {file = "numpy-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ea9e48336a402551f52cd8f593343699003d2353daa4b72ce8d34f66b722070"},
+ {file = "numpy-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ccb7336eaf0e77c1635b232c141846493a588ec9ea777a7c24d7166bb8533ae"},
+ {file = "numpy-2.3.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0bb3a4a61e1d327e035275d2a993c96fa786e4913aa089843e6a2d9dd205c66a"},
+ {file = "numpy-2.3.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:e344eb79dab01f1e838ebb67aab09965fb271d6da6b00adda26328ac27d4a66e"},
+ {file = "numpy-2.3.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:467db865b392168ceb1ef1ffa6f5a86e62468c43e0cfb4ab6da667ede10e58db"},
+ {file = "numpy-2.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:afed2ce4a84f6b0fc6c1ce734ff368cbf5a5e24e8954a338f3bdffa0718adffb"},
+ {file = "numpy-2.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0025048b3c1557a20bc80d06fdeb8cc7fc193721484cca82b2cfa072fec71a93"},
+ {file = "numpy-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5ee121b60aa509679b682819c602579e1df14a5b07fe95671c8849aad8f2115"},
+ {file = "numpy-2.3.1-cp311-cp311-win32.whl", hash = "sha256:a8b740f5579ae4585831b3cf0e3b0425c667274f82a484866d2adf9570539369"},
+ {file = "numpy-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4580adadc53311b163444f877e0789f1c8861e2698f6b2a4ca852fda154f3ff"},
+ {file = "numpy-2.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:ec0bdafa906f95adc9a0c6f26a4871fa753f25caaa0e032578a30457bff0af6a"},
+ {file = "numpy-2.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2959d8f268f3d8ee402b04a9ec4bb7604555aeacf78b360dc4ec27f1d508177d"},
+ {file = "numpy-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:762e0c0c6b56bdedfef9a8e1d4538556438288c4276901ea008ae44091954e29"},
+ {file = "numpy-2.3.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:867ef172a0976aaa1f1d1b63cf2090de8b636a7674607d514505fb7276ab08fc"},
+ {file = "numpy-2.3.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:4e602e1b8682c2b833af89ba641ad4176053aaa50f5cacda1a27004352dde943"},
+ {file = "numpy-2.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8e333040d069eba1652fb08962ec5b76af7f2c7bce1df7e1418c8055cf776f25"},
+ {file = "numpy-2.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e7cbf5a5eafd8d230a3ce356d892512185230e4781a361229bd902ff403bc660"},
+ {file = "numpy-2.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1b8f26d1086835f442286c1d9b64bb3974b0b1e41bb105358fd07d20872952"},
+ {file = "numpy-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ee8340cb48c9b7a5899d1149eece41ca535513a9698098edbade2a8e7a84da77"},
+ {file = "numpy-2.3.1-cp312-cp312-win32.whl", hash = "sha256:e772dda20a6002ef7061713dc1e2585bc1b534e7909b2030b5a46dae8ff077ab"},
+ {file = "numpy-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfecc7822543abdea6de08758091da655ea2210b8ffa1faf116b940693d3df76"},
+ {file = "numpy-2.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:7be91b2239af2658653c5bb6f1b8bccafaf08226a258caf78ce44710a0160d30"},
+ {file = "numpy-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25a1992b0a3fdcdaec9f552ef10d8103186f5397ab45e2d25f8ac51b1a6b97e8"},
+ {file = "numpy-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dea630156d39b02a63c18f508f85010230409db5b2927ba59c8ba4ab3e8272e"},
+ {file = "numpy-2.3.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bada6058dd886061f10ea15f230ccf7dfff40572e99fef440a4a857c8728c9c0"},
+ {file = "numpy-2.3.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:a894f3816eb17b29e4783e5873f92faf55b710c2519e5c351767c51f79d8526d"},
+ {file = "numpy-2.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:18703df6c4a4fee55fd3d6e5a253d01c5d33a295409b03fda0c86b3ca2ff41a1"},
+ {file = "numpy-2.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5902660491bd7a48b2ec16c23ccb9124b8abfd9583c5fdfa123fe6b421e03de1"},
+ {file = "numpy-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:36890eb9e9d2081137bd78d29050ba63b8dab95dff7912eadf1185e80074b2a0"},
+ {file = "numpy-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a780033466159c2270531e2b8ac063704592a0bc62ec4a1b991c7c40705eb0e8"},
+ {file = "numpy-2.3.1-cp313-cp313-win32.whl", hash = "sha256:39bff12c076812595c3a306f22bfe49919c5513aa1e0e70fac756a0be7c2a2b8"},
+ {file = "numpy-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d5ee6eec45f08ce507a6570e06f2f879b374a552087a4179ea7838edbcbfa42"},
+ {file = "numpy-2.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c4d9e0a8368db90f93bd192bfa771ace63137c3488d198ee21dfb8e7771916e"},
+ {file = "numpy-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b0b5397374f32ec0649dd98c652a1798192042e715df918c20672c62fb52d4b8"},
+ {file = "numpy-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c5bdf2015ccfcee8253fb8be695516ac4457c743473a43290fd36eba6a1777eb"},
+ {file = "numpy-2.3.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d70f20df7f08b90a2062c1f07737dd340adccf2068d0f1b9b3d56e2038979fee"},
+ {file = "numpy-2.3.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:2fb86b7e58f9ac50e1e9dd1290154107e47d1eef23a0ae9145ded06ea606f992"},
+ {file = "numpy-2.3.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:23ab05b2d241f76cb883ce8b9a93a680752fbfcbd51c50eff0b88b979e471d8c"},
+ {file = "numpy-2.3.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ce2ce9e5de4703a673e705183f64fd5da5bf36e7beddcb63a25ee2286e71ca48"},
+ {file = "numpy-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c4913079974eeb5c16ccfd2b1f09354b8fed7e0d6f2cab933104a09a6419b1ee"},
+ {file = "numpy-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:010ce9b4f00d5c036053ca684c77441f2f2c934fd23bee058b4d6f196efd8280"},
+ {file = "numpy-2.3.1-cp313-cp313t-win32.whl", hash = "sha256:6269b9edfe32912584ec496d91b00b6d34282ca1d07eb10e82dfc780907d6c2e"},
+ {file = "numpy-2.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:2a809637460e88a113e186e87f228d74ae2852a2e0c44de275263376f17b5bdc"},
+ {file = "numpy-2.3.1-cp313-cp313t-win_arm64.whl", hash = "sha256:eccb9a159db9aed60800187bc47a6d3451553f0e1b08b068d8b277ddfbb9b244"},
+ {file = "numpy-2.3.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ad506d4b09e684394c42c966ec1527f6ebc25da7f4da4b1b056606ffe446b8a3"},
+ {file = "numpy-2.3.1-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:ebb8603d45bc86bbd5edb0d63e52c5fd9e7945d3a503b77e486bd88dde67a19b"},
+ {file = "numpy-2.3.1-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:15aa4c392ac396e2ad3d0a2680c0f0dee420f9fed14eef09bdb9450ee6dcb7b7"},
+ {file = "numpy-2.3.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c6e0bf9d1a2f50d2b65a7cf56db37c095af17b59f6c132396f7c6d5dd76484df"},
+ {file = "numpy-2.3.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eabd7e8740d494ce2b4ea0ff05afa1b7b291e978c0ae075487c51e8bd93c0c68"},
+ {file = "numpy-2.3.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e610832418a2bc09d974cc9fecebfa51e9532d6190223bc5ef6a7402ebf3b5cb"},
+ {file = "numpy-2.3.1.tar.gz", hash = "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b"},
]
[[package]]
@@ -2003,14 +2003,14 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
[[package]]
name = "pygments"
-version = "2.19.1"
+version = "2.19.2"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
- {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"},
- {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"},
+ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"},
+ {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"},
]
[package.extras]
@@ -2552,27 +2552,27 @@ files = [
[[package]]
name = "sphinx"
-version = "7.4.7"
+version = "7.3.7"
description = "Python documentation generator"
optional = false
python-versions = ">=3.9"
groups = ["docs"]
files = [
- {file = "sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239"},
- {file = "sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe"},
+ {file = "sphinx-7.3.7-py3-none-any.whl", hash = "sha256:413f75440be4cacf328f580b4274ada4565fb2187d696a84970c23f77b64d8c3"},
+ {file = "sphinx-7.3.7.tar.gz", hash = "sha256:a4a7db75ed37531c05002d56ed6948d4c42f473a36f46e1382b0bd76ca9627bc"},
]
[package.dependencies]
alabaster = ">=0.7.14,<0.8.0"
-babel = ">=2.13"
-colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""}
-docutils = ">=0.20,<0.22"
+babel = ">=2.9"
+colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
+docutils = ">=0.18.1,<0.22"
imagesize = ">=1.3"
-Jinja2 = ">=3.1"
-packaging = ">=23.0"
-Pygments = ">=2.17"
-requests = ">=2.30.0"
-snowballstemmer = ">=2.2"
+Jinja2 = ">=3.0"
+packaging = ">=21.0"
+Pygments = ">=2.14"
+requests = ">=2.25.0"
+snowballstemmer = ">=2.0"
sphinxcontrib-applehelp = "*"
sphinxcontrib-devhelp = "*"
sphinxcontrib-htmlhelp = ">=2.0.0"
@@ -2582,8 +2582,28 @@ sphinxcontrib-serializinghtml = ">=1.1.9"
[package.extras]
docs = ["sphinxcontrib-websupport"]
-lint = ["flake8 (>=6.0)", "importlib-metadata (>=6.0)", "mypy (==1.10.1)", "pytest (>=6.0)", "ruff (==0.5.2)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-docutils (==0.21.0.20240711)", "types-requests (>=2.30.0)"]
-test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"]
+lint = ["flake8 (>=3.5.0)", "importlib_metadata", "mypy (==1.9.0)", "pytest (>=6.0)", "ruff (==0.3.7)", "sphinx-lint", "tomli", "types-docutils", "types-requests"]
+test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=6.0)", "setuptools (>=67.0)"]
+
+[[package]]
+name = "sphinx-rtd-theme"
+version = "1.3.0"
+description = "Read the Docs theme for Sphinx"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
+groups = ["docs"]
+files = [
+ {file = "sphinx_rtd_theme-1.3.0-py2.py3-none-any.whl", hash = "sha256:46ddef89cc2416a81ecfbeaceab1881948c014b1b6e4450b815311a89fb977b0"},
+ {file = "sphinx_rtd_theme-1.3.0.tar.gz", hash = "sha256:590b030c7abb9cf038ec053b95e5380b5c70d61591eb0b552063fbe7c41f0931"},
+]
+
+[package.dependencies]
+docutils = "<0.19"
+sphinx = ">=1.6,<8"
+sphinxcontrib-jquery = ">=4,<5"
+
+[package.extras]
+dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"]
[[package]]
name = "sphinxcontrib-applehelp"
@@ -2636,6 +2656,21 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
standalone = ["Sphinx (>=5)"]
test = ["html5lib", "pytest"]
+[[package]]
+name = "sphinxcontrib-jquery"
+version = "4.1"
+description = "Extension to include jQuery on newer Sphinx releases"
+optional = false
+python-versions = ">=2.7"
+groups = ["docs"]
+files = [
+ {file = "sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a"},
+ {file = "sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae"},
+]
+
+[package.dependencies]
+Sphinx = ">=1.8"
+
[[package]]
name = "sphinxcontrib-jsmath"
version = "1.0.1"
@@ -3021,4 +3056,4 @@ cffi = ["cffi (>=1.11)"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.11,<4.0"
-content-hash = "fb23da8c54844851b0f7b7af45edcf219dcf07c5286e26ea685379696387ac42"
+content-hash = "4a27def5957fabf64e9dc6885e04324be378588256e5247aae0874f1de895f98"
diff --git a/pyproject.toml b/pyproject.toml
index 610a15a..00e1b1e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -52,6 +52,7 @@ optional = true
[tool.poetry.group.docs.dependencies]
sphinx = "^7.1.2"
+sphinx_rtd_theme = "^1.3.0"
[[tool.poetry.source]]
name = "PyPI"
diff --git a/src/chemged/__init__.py b/src/chemged/__init__.py
index bc3f043..f9966b5 100644
--- a/src/chemged/__init__.py
+++ b/src/chemged/__init__.py
@@ -3,6 +3,7 @@
__version__ = "0.1.0a0"
__author__ = "James Wellnitz"
+from .chem_utils import mol_to_nx
from .chemged import ApproximateChemicalGED
from .cost import ChemicalGEDCostMatrix, UniformElementCostMatrix
@@ -11,4 +12,5 @@
"ChemicalGEDCostMatrix",
"UniformElementCostMatrix",
"ApproximateChemicalGED",
+ "mol_to_nx",
]
diff --git a/src/chemged/chem_utils.py b/src/chemged/chem_utils.py
index 311ddda..cdd8049 100644
--- a/src/chemged/chem_utils.py
+++ b/src/chemged/chem_utils.py
@@ -1,7 +1,8 @@
+"""Utilities for converting between RDKit Mol objects and NetworkX Graphs."""
+
from typing import Literal, Union, overload
import networkx as nx
-
from rdkit import Chem
from rdkit.rdBase import BlockLogs
@@ -98,14 +99,13 @@ def mol_to_nx(mol: Chem.Mol) -> nx.Graph:
is_aromatic=atom.GetIsAromatic(),
is_in_ring=atom.IsInRing(),
hydrogen=atom.GetTotalNumHs(),
- degree=atom.GetDegree()
+ degree=atom.GetDegree(),
+ formal_charge=atom.GetFormalCharge(),
)
if atom.GetIdx() > max_idx:
max_idx = atom.GetIdx()
for bond in mol.GetBonds():
- graph.add_edge(bond.GetBeginAtomIdx(),
- bond.GetEndAtomIdx(),
- bond_type=bond.GetBondType())
+ graph.add_edge(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx(), bond_type=bond.GetBondType())
return graph
@@ -128,9 +128,9 @@ def nx_to_mol(graph: nx.Graph) -> Chem.Mol:
Chem.Mol
"""
mol = Chem.RWMol()
- atomic_nums = nx.get_node_attributes(graph, 'atomic_num')
- node_is_aromatics = nx.get_node_attributes(graph, 'is_aromatic')
- node_hybridization = nx.get_node_attributes(graph, 'hybridization')
+ atomic_nums = nx.get_node_attributes(graph, "atomic_num")
+ node_is_aromatics = nx.get_node_attributes(graph, "is_aromatic")
+ node_hybridization = nx.get_node_attributes(graph, "hybridization")
node_to_idx = {}
for node in graph.nodes():
a = Chem.Atom(atomic_nums[node])
@@ -139,7 +139,7 @@ def nx_to_mol(graph: nx.Graph) -> Chem.Mol:
idx = mol.AddAtom(a)
node_to_idx[node] = idx
- bond_types = nx.get_edge_attributes(graph, 'bond_type')
+ bond_types = nx.get_edge_attributes(graph, "bond_type")
for edge in graph.edges():
first, second = edge
idx_first = node_to_idx[first]
diff --git a/src/chemged/chemged.py b/src/chemged/chemged.py
index ae1d090..1c892ac 100644
--- a/src/chemged/chemged.py
+++ b/src/chemged/chemged.py
@@ -50,7 +50,7 @@ class ApproximateChemicalGED:
\\end{bmatrix}
where :math:`c_{n,m}` is the cost of substituting node :math:`n` in graph :math:`n1`
- with node :math:`m` in graph :math:`n2, :math:`c_{n,\\epsilon}` is the cost of deleting
+ with node :math:`m` in graph :math:`n2`, :math:`c_{n,\\epsilon}` is the cost of deleting
a node from :math:`n1`, and :math:`c_{\\epsilon,m}` is the cost of inserting node
:math:`m` from :math:`n2` into :math:`n1`.