canonicalSmiles() fails with "SMILES saver: bad cycle number: 100" for highly cyclic valid SMILES
- Dominant language
- C++
- Stars
- 406
- Forks
- 134
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 24
Description
**Summary**
`canonicalSmiles()` fails with `SMILES saver: bad cycle number: 100` for a valid highly cyclic molecule.
Indigo successfully parses the molecule and can serialize it with `smiles()`. The failure occurs only when generating the canonical SMILES.
The example molecule is PubChem CID 20652954.
**Steps to Reproduce**
1. Install the Indigo Python bindings for Indigo 1.45.0:
```bash
pip install 'epam.indigo==1.45.0'
```
2. Run the following script:
```python
import indigo
SMILES = (
"C1C2C3C4C5C6C7C8C9C%10CC%11C%10%10C99C88C77C66C55C44C33C22C1"
"C1C22C33C44C55C66C77C88C99C%10%10C%11C%11C%10%10C99C88C77C66C55"
"C44C33C22C1C1C22C33C44C55C66C77C88C99C%10%10C%11C%11C%10%10C99"
"C88C77C66C55C44C33C22C1C1C22C33C44C55C66C77C88C99C%10%10C%11C%11"
"C%10%10C99C88C77C66C55C44C33C22C1C1C22C33C44C55C66C77C88C99C%10"
"%10C%11C%11C%10%10C99C88C77C66C55C44C33C22C1C1C22C33C44C55C66"
"C77C88C99C%10%10C%11C%11C%10%10C99C88C77C66C55C44C33C22C1C1C22"
"C33C44C55C66C77C88C99C%10%10C%11C%11C%10%10C99C88C77C66C55C44"
"C33C22C1C1C22C33C44C55C66C77C88C99C%10%10C%11C%11C%10%10C99C88"
"C77C66C55C44C33C22C1C1C22C33C44C55C66C77C88C99C%10%10C%11C%11"
"C%10%10C99C88C77C66C55C44C33C22C1C1C22C33C44C55C66C77C88C99C%10"
"%10C%11C%11C%10%10C99C88C77C66C55C44C33C22C1C1C22C33C44C55C66"
"C77C88C99C%10%10C%11C%11C%10%10C99C88C77C66C55C44C33C22C1CC2C3"
"C4C5C6C7C8C9C%10C%11"
)
ind = indigo.Indigo()
mol = ind.loadMolecule(SMILES)
print("atoms:", mol.countAtoms())
print("bonds:", mol.countBonds())
print("ordinary smiles:", mol.smiles())
print("canonical smiles:", mol.canonicalSmiles())
```
The molecule loads successfully and `smiles()` also succeeds:
```text
atoms: 231
bonds: 430
ordinary smiles: C1C2C3C4C5C6C7C8C9C%10C%11C%12C%13...
```
`canonicalSmiles()` then fails with:
```text
indigo.indigo.indigo_exception.IndigoException:
SMILES saver: bad cycle number: 100
```
**Actual behavior**
Indigo successfully:
* parses the input SMILES;
* creates a molecule containing 231 atoms and 430 bonds;
* serializes the same molecule with `smiles()`.
Only `canonicalSmiles()` fails.
The ordinary SMILES generated by Indigo uses ring closure numbers well below 100, so the graph itself does not inherently require more than 99 simultaneously available ring closure numbers.
The exception appears to come from the traversal/order selected specifically during canonical SMILES generation. That traversal eventually attempts to allocate ring closure number 100, which `SmilesSaver::_writeCycleNumber()` rejects.
In Indigo 1.45.0:
```cpp
void SmilesSaver::_writeCycleNumber(int n) const
{
if (n > 0 && n < 10)
_output.printf("%d", n);
else if (n >= 10 && n < 100)
_output.printf("%%%2d", n);
else
throw Error("bad cycle number: %d", n);
}
```
The original PubChem SMILES uses only ring closure numbers 1 through 11.
**Expected behavior**
`canonicalSmiles()` should successfully serialize this molecule, since Indigo can parse the structure and serialize the same graph as ordinary SMILES.
Ideally, a fix would preserve the existing canonical SMILES output for molecules that Indigo already canonicalizes successfully. Applications may persist Indigo canonical SMILES and use exact string equality for identity lookups, so changing canonical output for previously supported molecules could be disruptive.
A compatible fix that only changes behavior for structures whose current canonical traversal cannot be serialized would therefore be preferable.
**Environment details:**
* Back-end version: Indigo 1.45.0
* Binding: Python (`epam.indigo==1.45.0`)
* Python: 3.12
* OS: Linux
**Attachments**
No attachment is required; the SMILES in the reproduction above is sufficient.
PubChem CID: `20652954`
**Additional context**
PubChem currently returns identical `SMILES` and `ConnectivitySMILES` values for CID 20652954, so switching between those representations does not avoid the failure.
This appears to be a canonical SMILES serialization issue rather than an invalid-input or general molecule-complexity issue:
```text
loadMolecule() succeeds
smiles() succeeds
canonicalSmiles() fails with "bad cycle number: 100"
```
The same molecule was also tested with RDKit. RDKit successfully parses the structure as 231 atoms and 430 bonds and successfully generates a canonical SMILES. Its canonical serialization uses ring closure labels only up to 56.
That comparison is not intended to suggest using RDKit's canonical SMILES as a substitute for Indigo's. Canonical SMILES strings are toolkit-specific, and applications may depend on Indigo's existing canonical output for exact identity matching. It does, however, show that a deterministic canonical serialization of this graph is possible without exceeding the two-digit ring closure range.
The stronger Indigo-only observation is that `smiles()` can already serialize this exact molecule successfully. The failure is specific to the traversal/order used by `canonicalSmiles()`.
Contributor guide
No contributing guide indexed for this repository
Research direction
Reproduce the failure with the provided Python script and the 231-atom SMILES, then inspect SmilesSaver::_writeCycleNumber() and the canonicalSmiles() traversal/order that reaches cycle number 100. Compare this path with ordinary smiles() serialization. Done means canonicalSmiles() succeeds for CID 20652954 while preserving existing canonical output for molecules that already work.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, python
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100