Repository: openforcefield/openff-toolkit

Issue:
isIsomorphic function behaves oddly
I tested the isomorphic function on a simple molecule and found that while it is isomorphic to itself (duh...) I do not get the atom correspondence printed if I copy the molecule by converting it to smiles first. This does not match the API description where the function should return the atom pairs whenever two molecules are isomorphic. I attached a notebook showing the example. Note that this does not happen for all molecules, there is a simpler smiles String in the notebook that does not show this issue. 

**To Reproduce**
[IsomorphicOddity.zip](https://github.com/openforcefield/openff-toolkit/files/11427996/IsomorphicOddity.zip)
[condalist.txt](https://github.com/openforcefield/openff-toolkit/files/11428035/condalist.txt)

**Computing environment (please complete the following information):**
RHEL - Operating system, conda list output attached

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/openforcefield/openff-toolkit/pull/1609?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openforcefield) Report
> Merging [#1609](https://app.codecov.io/gh/openforcefield/openff-toolkit/pull/1609?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openforcefield) (22beebf) into [main](https://app.codecov.io/gh/openforcefield/openff-toolkit/commit/e4a3b2ab6acc529c6138617045baab9332d9dab4?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=openforcefield) (e4a3b2a) will **decrease** coverage by `11.91%`.
> The diff coverage is `100.00%`.

<details><summary>Additional details and impacted files</summary>


</details>
mattwthompson: Test failures are fixed by #1606 

Patch:
diff --git a/openff/toolkit/topology/molecule.py b/openff/toolkit/topology/molecule.py
index ce0c6972..13536058 100644
--- a/openff/toolkit/topology/molecule.py
+++ b/openff/toolkit/topology/molecule.py
@@ -1941,8 +1941,9 @@ class FrozenMolecule(Serializable):
             The second molecule to test for isomorphism.
 
         return_atom_map: bool, default=False, optional
-            Return a ``dict`` containing the atomic mapping instead of a
-            ``bool``.
+            Return a ``dict`` containing the atomic mapping, otherwise ``None``.
+            Only processed if inputs are isomorphic, will always return ``None`` if
+            inputs are not isomorphic.
 
         aromatic_matching: bool, default=True, optional
             If ``False``, aromaticity of graph nodes and edges are ignored for
@@ -2025,7 +2026,10 @@ class FrozenMolecule(Serializable):
         # Do a quick check to see whether the inputs are totally identical (including being in the same atom order)
         if isinstance(mol1, FrozenMolecule) and isinstance(mol2, FrozenMolecule):
             if mol1._is_exactly_the_same_as(mol2):
-                return True, {i: i for i in range(mol1.n_atoms)}
+                if return_atom_map:
+                    return True, {i: i for i in range(mol1.n_atoms)}
+                else:
+                    return True, None
 
         # Build the user defined matching functions
         def node_match_func(x, y):


Test patch:
diff --git a/openff/toolkit/tests/test_molecule.py b/openff/toolkit/tests/test_molecule.py
index 426384ac..84ff87dd 100644
--- a/openff/toolkit/tests/test_molecule.py
+++ b/openff/toolkit/tests/test_molecule.py
@@ -1746,6 +1746,37 @@ class TestMolecule:
             create_ethanol(),
         )[0]
 
+    def test_exactly_the_same_short_circuit_return_atom_map(self):
+        SMILES = "c1ncccc1COCC"
+        ISOMORPHIC_SMILES = "CCOCc1cccnc1"
+
+        molecule = Molecule.from_smiles(SMILES)
+        exactly_the_same = Molecule.from_smiles(SMILES)
+        not_exactly_the_same = Molecule.from_smiles(ISOMORPHIC_SMILES)
+
+        atom_map = {index: index for index in range(molecule.n_atoms)}
+
+        # If not returning the map, isomorphic inputs should always return (True, None)
+        assert Molecule.are_isomorphic(
+            molecule, exactly_the_same, return_atom_map=False
+        ) == (True, None)
+        assert Molecule.are_isomorphic(
+            molecule, not_exactly_the_same, return_atom_map=False
+        ) == (True, None)
+
+        # If returning the mapping, isomorphic inputs should always return True and some mapping ...
+        assert Molecule.are_isomorphic(
+            molecule, exactly_the_same, return_atom_map=True
+        ) == (True, atom_map)
+
+        # but the ordering is not guaranteed, so just check that the map is a dict of the right length
+        result, mapping = Molecule.are_isomorphic(
+            molecule, not_exactly_the_same, return_atom_map=True
+        )
+        assert result
+        assert isinstance(mapping, dict)
+        assert len(mapping) == molecule.n_atoms
+
     def test_graph_and_molecule_inputs(self):
         molecule = create_ethanol()
         graph = molecule.to_networkx()