Skip to content

add the modern CIP labeller - #34

Closed
AdrianM0 wants to merge 10 commits into
mainfrom
dev
Closed

add the modern CIP labeller#34
AdrianM0 wants to merge 10 commits into
mainfrom
dev

Conversation

@AdrianM0

@AdrianM0 AdrianM0 commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Summary by Sourcery

Integrate RDKit's modern rdCIPLabeler-based stereochemistry handling to improve CIP R/S and E/Z assignment while keeping legacy behavior as a fallback for unsanitized input.

Enhancements:

  • Use rdCIPLabeler to derive authoritative R/S atom chirality and E/Z bond labels instead of relying solely on legacy stereochemistry perception.
  • Guard rdCIPLabeler usage with try/except blocks to gracefully degrade to legacy labels when CIP assignment fails.

@sourcery-ai

sourcery-ai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Reviewer's Guide

Integrates RDKit's modern rdCIPLabeler into stereochemistry handling to replace legacy CIP codes while preserving compatibility, and updates atom and bond stereo extraction logic to rely on CIP properties with robust fallbacks for unsanitized inputs.

Sequence diagram for updated stereochemistry and CIP label handling

sequenceDiagram
  participant read_smiles
  participant Chem
  participant rdCIPLabeler
  participant rdmol

  read_smiles->>Chem: AssignStereochemistry(rdmol, force=True, cleanIt=True)
  read_smiles->>rdCIPLabeler: AssignCIPLabels(rdmol)
  alt rdCIPLabeler succeeds
    read_smiles->>rdmol: GetAtoms()
    loop atoms
      read_smiles->>rdmol: GetAtoms()
      read_smiles->>rdmol: GetAtoms()
      rdmol-->>read_smiles: atom
      read_smiles->>atom: HasProp(_CIPCode)
      read_smiles->>atom: GetProp(_CIPCode)
    end
    read_smiles->>rdmol: GetBonds()
    loop bonds
      rdmol-->>read_smiles: bond
      read_smiles->>bond: GetPropsAsDict()
      read_smiles->>bond: GetStereo()
    end
  else rdCIPLabeler raises Exception
    read_smiles-->>read_smiles: fall back to legacy CIP labels
  end
Loading

File-Level Changes

Change Details Files
Use rdCIPLabeler to assign CIP labels and derive chiral center R/S configuration from atom properties instead of legacy FindMolChiralCenters results.
  • Import rdCIPLabeler alongside Chem from RDKit.
  • Call Chem.AssignStereochemistry first to perceive stereo, then run rdCIPLabeler.AssignCIPLabels in a try/except to tolerate unsanitized molecules.
  • Build the chiral_centers mapping from atoms having _CIPCode properties restricted to R/S rather than from FindMolChiralCenters.
  • Remove special-case inversion of three-coordinate sulfur stereocenters now that modern CIP labeling handles them correctly.
src/openclatura/graph_io.py
Derive E/Z bond stereochemistry primarily from CIP _CIPCode on bonds, with fallback to legacy bond stereo enums when CIP labeling is unavailable.
  • Read bond _CIPCode from GetPropsAsDict and treat E/Z as authoritative when present.
  • Fallback to BondStereo.STEREOE/STEREOZ when CIP code is missing or not E/Z, otherwise set stereo to None.
  • Retain small-ring detection and downstream logic unchanged apart from the stereo source.
src/openclatura/graph_io.py
Apply rdCIPLabeler to temporary RDKit fragments used in small-ring local CIP determination, with graceful fallback to legacy labels on failure.
  • Import rdCIPLabeler in the small_ring_stereo module.
  • After AssignStereochemistry on the RDKit fragment, call rdCIPLabeler.AssignCIPLabels inside a try/except to handle unsanitized fragments.
  • Keep the existing mapping from RDKit atom indices to local atom indices and the result assembly logic unchanged, now using modern CIP labels when available.
src/openclatura/small_ring_stereo.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 48ac79a6-5f16-482c-ad85-ce3a59c3400f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The broad except Exception blocks around AssignCIPLabels make it hard to distinguish real RDKit/logic errors from unsanitized input; consider narrowing the exception type or at least logging unexpected failures so silent mislabelling is easier to detect.
  • For bond E/Z extraction, using bond.HasProp('_CIPCode')/bond.GetProp('_CIPCode') instead of GetPropsAsDict() would avoid per-bond dict creation and reduce overhead in large molecules while keeping the same behavior.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The broad `except Exception` blocks around `AssignCIPLabels` make it hard to distinguish real RDKit/logic errors from unsanitized input; consider narrowing the exception type or at least logging unexpected failures so silent mislabelling is easier to detect.
- For bond E/Z extraction, using `bond.HasProp('_CIPCode')`/`bond.GetProp('_CIPCode')` instead of `GetPropsAsDict()` would avoid per-bond dict creation and reduce overhead in large molecules while keeping the same behavior.

## Individual Comments

### Comment 1
<location path="src/openclatura/graph_io.py" line_range="65" />
<code_context>
+        # rdCIPLabeler rewrites bond enums to STEREOCIS/STEREOTRANS but stamps
+        # the authoritative E/Z label as _CIPCode; fall back to the enums when
+        # the labeler did not run.
+        stereo = bond.GetPropsAsDict().get("_CIPCode")
+        if stereo not in ("E", "Z"):
+            st = bond.GetStereo()
</code_context>
<issue_to_address>
**suggestion (performance):** Using GetPropsAsDict per bond may be unnecessarily expensive compared to HasProp/GetProp.

`GetPropsAsDict` creates a dict of all bond properties just to access `_CIPCode`. Using `HasProp`/`GetProp` avoids this per-bond allocation while preserving behavior:

```python
stereo = bond.GetProp("_CIPCode") if bond.HasProp("_CIPCode") else None
```
This is a small change but can improve performance when handling many bonds or large molecules.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

# rdCIPLabeler rewrites bond enums to STEREOCIS/STEREOTRANS but stamps
# the authoritative E/Z label as _CIPCode; fall back to the enums when
# the labeler did not run.
stereo = bond.GetPropsAsDict().get("_CIPCode")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Using GetPropsAsDict per bond may be unnecessarily expensive compared to HasProp/GetProp.

GetPropsAsDict creates a dict of all bond properties just to access _CIPCode. Using HasProp/GetProp avoids this per-bond allocation while preserving behavior:

stereo = bond.GetProp("_CIPCode") if bond.HasProp("_CIPCode") else None

This is a small change but can improve performance when handling many bonds or large molecules.

@AdrianM0
AdrianM0 marked this pull request as draft July 19, 2026 14:40
@AdrianM0 AdrianM0 closed this Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant