Skip to content

Edge type flags - #89

Open
vratins wants to merge 6 commits into
mainfrom
dev_edge_type_flags
Open

Edge type flags#89
vratins wants to merge 6 commits into
mainfrom
dev_edge_type_flags

Conversation

@vratins

@vratins vratins commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
  • Makes edge construction configurable: etypes, cutoff, max_neighbors, dynamic_edge_policy, knn_fallback_k on ProteinWaterUpdate, which previously hardcoded ALL_EDGE_TYPES. This is the dependency ConfidenceGVP needs.
  • get_active_edge_types(disable_ww, disable_wp) to ablate WW/WP edges; PW and PP always stay on.
  • build_knn_edgesbuild_dynamic_edges, supporting both radius and kNN queries, plus a fallback that reconnects nodes a radius query left with no edges in the case of sampling with a scaled gaussian.
  • Replaces --k_pw/--k_ww with --dynamic_edge_policy, --cutoff, --max_neighbors, --knn_fallback_k, --disable_ww, --disable_wp; inference.py reads the same keys back from config.json.

Summary by CodeRabbit

  • New Features

    • Added configurable dynamic graph edges using radius, KNN, or isolated-node rescue policies.
    • Added controls for distance cutoffs, fallback neighbors, sampling strategy, and per-edge neighbor limits.
    • Added options to independently disable water–water and water–protein connections.
    • Water-related edges can refresh during each forward pass, while protein–protein edges remain cached.
  • Documentation

    • Updated configuration and training guidance, including legacy setting mappings and new command-line options.
  • Bug Fixes

    • Improved handling of isolated nodes and invalid edge-policy configurations.

Copilot AI lite review requested due to automatic review settings August 4, 2026 23:09
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@vratins, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 78 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dbd95f39-f215-4e97-a7c4-cf6526312434

📥 Commits

Reviewing files that changed from the base of the PR and between a022818 and 5fe5b13.

📒 Files selected for processing (4)
  • README.md
  • scripts/inference.py
  • scripts/train.py
  • src/flow.py
📝 Walkthrough

Walkthrough

The PR replaces fixed KNN edge construction with configurable radius and KNN policies. It adds fallback rescue, edge-type ablations, model-owned configuration, CLI and inference wiring, README documentation, and expanded regression tests.

Changes

Dynamic edge construction

Layer / File(s) Summary
Policy resolution and dynamic edge builder
src/flow.py, tests/test_flow.py
build_dynamic_edges supports KNN and radius policies with batching, caps, deduplication, self-edge handling, and policy validation.
Model-level edge assembly and fallback
src/constants.py, src/flow.py, tests/test_flow.py, tests/test_forward.py
ProteinWaterUpdate applies active edge types, cached edges, relation-specific limits, dynamic policies, and isolated-node KNN rescue. FlowWaterGVP owns this configuration.
Training, inference, and configuration support
scripts/train.py, scripts/inference.py, README.md, tests/test_train_config.py
Training and inference expose dynamic-edge settings, ablations, fallbacks, and neighbor limits. Documentation and configuration replay tests cover the new behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TrainingCLI
  participant FlowWaterGVP
  participant ProteinWaterUpdate
  participant build_dynamic_edges
  TrainingCLI->>FlowWaterGVP: pass dynamic-edge configuration
  FlowWaterGVP->>ProteinWaterUpdate: initialize active edge settings
  ProteinWaterUpdate->>build_dynamic_edges: construct water interaction edges
  build_dynamic_edges-->>ProteinWaterUpdate: return dynamic edges
  ProteinWaterUpdate-->>FlowWaterGVP: return assembled graph
Loading

Possibly related PRs

  • diff-use/WaterFlow#82: Both changes modify KNN edge construction and edge-direction conventions in src/flow.py.
  • diff-use/WaterFlow#84: Both changes propagate sampling and dynamic-edge policy settings through FlowWaterGVP and FlowMatcher.

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title identifies the edge-type flag changes, which are a real part of the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev_edge_type_flags

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR makes edge construction in the flow model configurable (edge-type ablations and radius-vs-kNN dynamic graph building), and updates training/inference plumbing plus tests/docs to support replaying historical configs and new runtime edge policies.

Changes:

  • Introduces dynamic edge construction controls (policy, cutoff, max_neighbors, fallback k, and WW/WP ablations) and threads them through FlowWaterGVP/ProteinWaterUpdate.
  • Replaces the previous KNN-only edge builder with a unified radius/KNN builder plus optional “rescue” edges for isolated nodes.
  • Updates CLI/config loading, tests, and README documentation to reflect the new edge configuration surface.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/test_train_config.py Adds regression test to ensure recorded configs with dynamic_edge_policy: "auto" replay successfully and map to the intended runtime policy/etypes.
tests/test_forward.py Updates forward-pass test to validate dynamic edges via the new build_edges() signature (no explicit k args).
tests/test_flow.py Expands unit tests for radius/KNN edge building semantics, row conventions, fallback behavior, and policy resolution.
src/flow.py Implements resolve_edge_policy, build_dynamic_edges, edge-type configuration/ablation, and fallback (“rescue”) logic in ProteinWaterUpdate.
src/constants.py Adds get_active_edge_types() helper for WW/WP ablation while keeping PW/PP always enabled.
scripts/train.py Replaces old k-only flags with a richer edge configuration CLI and wires args into model construction.
scripts/inference.py Loads the same edge configuration keys from config.json when reconstructing models for inference.
README.md Updates edge-type descriptions and adds an “Edge Construction” section documenting the new policy controls.
Suppressed comments (2)

src/flow.py:259

  • build_dynamic_edges currently treats any policy other than "knn" as the radius path. That means typos (or accidentally passing "knn_if_isolated") silently change behavior instead of failing fast.
    if policy == "knn":
        # Asked for each destination's nearest sources, so sources come back second.
        dst_idx, src_idx = knn(
            x=src_pos, y=dst_pos, k=k, batch_x=batch_src, batch_y=batch_dst
        )

README.md:310

  • The CLI-flag summary table is out of sync with the implemented arguments: it lists --dynamic_edge_policy default as radius and only mentions radius/knn, but the CLI defaults to auto and also supports knn_if_isolated; additionally, --max_neighbors and --k_wp (and the knn k's) are missing.
| `--dynamic_edge_policy` | `radius` | How water-touching edges are built: `radius` or `knn` (see [Edge Construction](#edge-construction)) |
| `--cutoff` | `8.0` | Distance cutoff in Å for radius edges |
| `--knn_fallback_k` | `8` | Nearest neighbours attached to waters stranded by the radius query; `0` disables |
| `--disable_ww` | `false` | Ablate water→water edges |
| `--disable_wp` | `false` | Ablate water→protein edges |

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/flow.py Outdated
Comment thread src/flow.py
Comment thread README.md
Comment thread README.md Outdated
Copilot AI review requested due to automatic review settings August 4, 2026 23:13

@coderabbitai coderabbitai 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.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/train.py (1)

1101-1108: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass sampling_strategy to FlowMatcher.

build_model resolves dynamic_edge_policy from the run's sampling strategy. FlowMatcher here keeps its "uniform_ball" default. If a run selects scaled_gaussian, the model resolves "auto" to knn_if_isolated while the matcher still samples from the uniform ball prior. The two settings then describe different runs.

🐛 Proposed fix
     flow_matcher = FlowMatcher(
         model=model,
         p_self_cond=args.p_self_cond,
         use_distortion=args.use_distortion,
         p_distort=args.p_distort,
         t_distort=args.t_distort,
         sigma_distort=args.sigma_distort,
+        sampling_strategy=args.sampling_strategy,
     )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/train.py` around lines 1101 - 1108, Pass the run’s sampling_strategy
argument into the FlowMatcher construction alongside the existing distortion
parameters, ensuring it uses the same resolved strategy as build_model. Preserve
the current argument wiring and FlowMatcher behavior for callers whose strategy
is unspecified.
🧹 Nitpick comments (3)
src/flow.py (2)

673-684: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document sampling_strategy in the constructor docstring.

The signature adds sampling_strategy, and the value decides how "auto" resolves. The Args block does not list it. Readers cannot see that the prior affects edge construction.

📝 Proposed docstring addition
             dynamic_edge_policy: How water-touching edges are built, one of
                 DYNAMIC_EDGE_POLICIES. Default: "radius"
+            sampling_strategy: Prior the run uses. Consulted only to resolve
+                a "auto" policy; see `resolve_edge_policy`. Default: "uniform_ball"
             knn_fallback_k: Nearest neighbours attached to waters the radius
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/flow.py` around lines 673 - 684, Update the constructor docstring’s Args
section to document the sampling_strategy parameter, including that it controls
how the “auto” strategy is resolved and affects edge construction. Place it
alongside the other edge-construction configuration arguments and describe its
default or accepted values using the existing symbols.

527-537: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a PP-specific neighbour count instead of k_pw for the dynamic protein-protein fallback.

Under policy="knn", this branch builds protein->protein edges with k=self.k_pw. k_pw is documented as the count for protein->water edges. The two relations have different densities, so the reuse couples an unrelated setting to PP. The branch runs only when the dataset carries no cached PP edges, but that is exactly the path where the value matters.

Add a k_pp parameter, or state the reuse in a comment so it is a deliberate choice.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/flow.py` around lines 527 - 537, Update the protein-protein fallback in
the dynamic edge construction around build_dynamic_edges to use a dedicated k_pp
neighbor-count parameter rather than k_pw. Add and propagate k_pp through the
relevant flow configuration and call sites, preserving k_pw exclusively for
protein-water edges.
tests/test_flow.py (1)

422-461: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the water->protein rescue axis.

test_radius_strands_far_water_and_fallback_rescues_it covers _add_knn_fallback with isolate_axis=1. The isolate_axis=0 branch remaps rows differently: it queries with the isolated sources and then swaps the returned rows. No test pins that remapping, so a row swap there would pass silently.

Add a case that strands a protein atom far from every water and asserts that the rescued EDGE_WP edges keep row 0 inside the water index range and row 1 inside the protein index range.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_flow.py` around lines 422 - 461, Add a test alongside
test_radius_strands_far_water_and_fallback_rescues_it that places one protein
atom far from all waters, enables radius isolation rescue with knn_fallback_k >
0, and inspects rescued EDGE_WP edges. Assert row 0 contains only valid water
indices and row 1 contains only valid protein indices, pinning the
isolate_axis=0 query-and-row-swap behavior in _add_knn_fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 211-231: Update the Edge Construction section to document all four
dynamic_edge_policy values: auto, radius, knn, and knn_if_isolated. Describe
that auto resolves to knn_if_isolated for scaled_gaussian sampling and otherwise
follows the standard default behavior, and state that stranded-water rescue via
knn_fallback_k requires the resolved knn_if_isolated policy; positive fallback
values do not rescue under plain radius.
- Around line 306-308: Update the README options table to match
scripts/train.py: document dynamic_edge_policy’s default as auto and include
auto, radius, knn, and knn_if_isolated as valid policies. Revise the
knn_fallback_k description to state that the rescue applies only to
knn_if_isolated runs, while preserving its default and disabled value.

In `@scripts/inference.py`:
- Around line 279-281: Update the FlowMatcher construction in
scripts/inference.py to pass the recorded sampling_strategy from config,
defaulting to "uniform_ball" consistently with the earlier configuration
handling. Preserve the existing model and p_self_cond arguments while ensuring
integration uses the training run’s selected prior.

In `@scripts/train.py`:
- Around line 628-638: The training CLI must define the sampling_strategy
argument before it is read. In scripts/train.py lines 628-638, retain
sampling_strategy=args.sampling_strategy in the FlowMatcher construction; in
scripts/train.py lines 1101-1108, add the parse_args option with uniform_ball
and scaled_gaussian choices; and in scripts/inference.py lines 279-281, make the
corresponding argument wiring consistent as required by the existing FlowMatcher
configuration.

In `@src/flow.py`:
- Around line 498-499: Update the comment above rescue_isolated in the relevant
flow method to accurately describe that the extra pass applies to both
protein→water and water→protein edges, while preserving the rescue assignment
itself.
- Around line 265-283: Update the homogeneous branch in build_dynamic_edges to
return source indices in row 0 by using the appropriate radius_graph flow or
flipping its output. Apply max_neighbors through the source-side neighbor limit
rather than max_num_neighbors, while preserving the existing candidate cap and
non-homogeneous radius behavior.

---

Outside diff comments:
In `@scripts/train.py`:
- Around line 1101-1108: Pass the run’s sampling_strategy argument into the
FlowMatcher construction alongside the existing distortion parameters, ensuring
it uses the same resolved strategy as build_model. Preserve the current argument
wiring and FlowMatcher behavior for callers whose strategy is unspecified.

---

Nitpick comments:
In `@src/flow.py`:
- Around line 673-684: Update the constructor docstring’s Args section to
document the sampling_strategy parameter, including that it controls how the
“auto” strategy is resolved and affects edge construction. Place it alongside
the other edge-construction configuration arguments and describe its default or
accepted values using the existing symbols.
- Around line 527-537: Update the protein-protein fallback in the dynamic edge
construction around build_dynamic_edges to use a dedicated k_pp neighbor-count
parameter rather than k_pw. Add and propagate k_pp through the relevant flow
configuration and call sites, preserving k_pw exclusively for protein-water
edges.

In `@tests/test_flow.py`:
- Around line 422-461: Add a test alongside
test_radius_strands_far_water_and_fallback_rescues_it that places one protein
atom far from all waters, enables radius isolation rescue with knn_fallback_k >
0, and inspects rescued EDGE_WP edges. Assert row 0 contains only valid water
indices and row 1 contains only valid protein indices, pinning the
isolate_axis=0 query-and-row-swap behavior in _add_knn_fallback.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ba98ee21-f527-4166-aab9-b78c54fed4fd

📥 Commits

Reviewing files that changed from the base of the PR and between b28dd6e and 92ce106.

📒 Files selected for processing (8)
  • README.md
  • scripts/inference.py
  • scripts/train.py
  • src/constants.py
  • src/flow.py
  • tests/test_flow.py
  • tests/test_forward.py
  • tests/test_train_config.py

Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread scripts/inference.py
Comment thread scripts/train.py
Comment thread src/flow.py
Comment thread src/flow.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Suppressed comments (8)

src/flow.py:256

  • build_dynamic_edges treats any policy value other than "knn" as the radius path, so typos or unexpected values silently change behavior. Since the docstring says the policy must be one of DYNAMIC_EDGE_POLICIES, this should be validated and raise early.
    # Same object
    homogeneous = src_pos is dst_pos

    if policy == "knn":
        # Asked for each destination's nearest sources, so sources come back second.

README.md:90

  • This says only PP edges are stored in the geometry cache and that every water-touching edge is rebuilt each forward pass, but ProteinWaterUpdate.build_edges explicitly reuses cached PW edges when present (and there is a unit test asserting this behavior). The README should mention this exception so users understand when PW is rebuilt vs reused.
- Only PP edges are stored in the geometry cache; every water-touching edge is
  rebuilt each forward pass, since water positions move during integration. See
  [Edge Construction](#edge-construction)

README.md:215

  • The Edge Construction table omits auto and knn_if_isolated, and labels radius as the default, but scripts/train.py defaults --dynamic_edge_policy to auto and the code supports knn_if_isolated. Updating the table will prevent confusion when replaying configs or using CLI flags.
| `--dynamic_edge_policy` | Behaviour |
|-------------------------|-----------|
| `radius` (default) | Connect every pair within `--cutoff`, capped at `--max_neighbors` per source |
| `knn` | Connect a fixed number of nearest neighbours (`--k_pw`, `--k_ww`, `--k_wp`) |

README.md:231

  • This blockquote implies that whether stranded waters are rescued is now solely --knn_fallback_k’s job, but the code only enables rescue when the resolved policy is knn_if_isolated (including auto with scaled_gaussian). With dynamic_edge_policy=radius, rescue stays off even if knn_fallback_k>0.
> Configs written before the radius/KNN split recorded a three-valued
> `dynamic_edge_policy` (`auto`, `radius`, `knn_if_isolated`). All three built a
> radius graph, so they load and map to `radius`; whether stranded waters are
> rescued is now `--knn_fallback_k`'s job.

README.md:306

  • The documented default for --dynamic_edge_policy is radius, but the CLI default in scripts/train.py is auto. The README should match the actual flag default (and ideally list all supported values) to avoid confusion when starting new runs.
| `--dynamic_edge_policy` | `radius` | How water-touching edges are built: `radius` or `knn` (see [Edge Construction](#edge-construction)) |

src/flow.py:499

  • The comment says only protein→water edges get the fallback pass, but the code also applies _add_knn_fallback to water→protein edges when rescue is enabled. Please update the comment so it matches the actual behavior (or restrict the rescue logic to PW only if that was the intent).
        # Only protein-water edges get the extra pass; waters keep protein context anyway.
        rescue = self.rescue_isolated

scripts/train.py:256

  • The --knn_fallback_k help text implies the rescue runs under --dynamic_edge_policy radius, but the implementation only enables rescue when the resolved policy is knn_if_isolated (including auto with scaled_gaussian). As written, the CLI help is misleading.
        help=(
            "Nearest neighbours attached to waters the radius query stranded; "
            "0 disables the rescue. Ignored under --dynamic_edge_policy knn "
            "(default: 8)"
        ),

README.md:223

  • This paragraph says --knn_fallback_k rescues stranded waters “under radius”, but the implementation explicitly does not rescue under plain radius (only under knn_if_isolated / auto+scaled_gaussian). The README should reflect the actual gating so users don’t expect radius+fallback to work.

This issue also appears on line 228 of the same file.

`--knn_fallback_k` repairs that. Under `radius`, any water the query stranded is
reconnected to that many nearest protein atoms regardless of distance. Set it to
`0` to disable. It has no effect under `knn`, which cannot strand a node.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Suppressed comments (3)

README.md:214

  • The Edge Construction table says radius is the default and only documents radius/knn, but the CLI default in scripts/train.py is auto and the code also supports knn_if_isolated. This mismatch will confuse users about what happens when they omit --dynamic_edge_policy.
| `--dynamic_edge_policy` | Behaviour |
|-------------------------|-----------|
| `radius` (default) | Connect every pair within `--cutoff`, capped at `--max_neighbors` per source |
| `knn` | Connect a fixed number of nearest neighbours (`--k_pw`, `--k_ww`, `--k_wp`) |

src/flow.py:242

  • build_dynamic_edges documents policy as one of DYNAMIC_EDGE_POLICIES, but the implementation only special-cases "knn" and otherwise falls back to radius behavior. That means typos (or passing "knn_if_isolated") silently change behavior instead of failing fast, and the current homogenous-graph detection uses src_pos is dst_pos which misses cases where the same tensor is passed via a different view/copy, leaving self-loops/cap logic inconsistent.
        policy: One of DYNAMIC_EDGE_POLICIES.
        k: Nearest neighbours per destination, used when policy is "knn".
        r: Distance cutoff in Angstroms, used when policy is "radius".
        max_neighbors: Per-source cap on radius results.
        batch_src: (N_src,) batch assignment for source nodes, or None.

README.md:223

  • The README says --knn_fallback_k rescues stranded waters under radius, but the implementation only enables the rescue under knn_if_isolated (or auto resolving to it) and explicitly disables rescue for plain radius even when knn_fallback_k > 0. The docs should match the actual behavior so users can reason about isolation handling.
`--knn_fallback_k` repairs that. Under `radius`, any water the query stranded is
reconnected to that many nearest protein atoms regardless of distance. Set it to
`0` to disable. It has no effect under `knn`, which cannot strand a node.

@vratins vratins mentioned this pull request Aug 5, 2026
Copilot AI review requested due to automatic review settings August 13, 2026 06:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Suppressed comments (4)

README.md:89

  • README currently states PW edges are always built at runtime and that every water-touching edge is rebuilt each forward pass, but ProteinWaterUpdate.build_edges explicitly supports using cached PW edges verbatim (e.g. the confidence pipeline). The docs should reflect that PW may be precomputed in some datasets/configurations.
  - `('protein', 'pw', 'water')`: protein to water — built at runtime
  - `('water', 'wp', 'protein')`: water to protein — built at runtime, ablatable
  - `('water', 'ww', 'water')`: water-water edges — built at runtime, ablatable
- Only PP edges are stored in the geometry cache; every water-touching edge is
  rebuilt each forward pass, since water positions move during integration. See

src/flow.py:255

  • homogeneous = src_pos is dst_pos only detects Python object identity. If callers pass two tensors that share the same underlying positions (e.g., views/clones/loaded copies), the function will treat the query as heterogeneous and may emit self-loops under the radius policy (and skip the self-loop filter under KNN). Use a storage/shape-based check instead so homogeneous graphs reliably drop self edges.
    # Same object
    homogeneous = src_pos is dst_pos

src/flow.py:677

  • dynamic_edge_policy is documented here as "one of DYNAMIC_EDGE_POLICIES", but the rest of the code/CLI also supports the recorded-config value "auto" (resolved via resolve_edge_policy). Updating this docstring avoids confusion for callers.
            dynamic_edge_policy: How water-touching edges are built, one of
                DYNAMIC_EDGE_POLICIES. Default: "radius"

src/flow.py:370

  • The docstring says PW/PP are always active, but etypes currently allows omitting them (including []), which can lead to missing required relations or even an empty GVPMultiEdgeConv setup. If PW/PP must always be on, validate that etypes includes them (or update the docstring to match the actual contract).
        unknown = [et for et in (etypes or []) if et not in ALL_EDGE_TYPES]
        if unknown:
            raise ValueError(
                f"etypes must be a subset of {ALL_EDGE_TYPES}, got unknown {unknown}"
            )

@DorisMai DorisMai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

A few minor questions/comments, address them if you can before merging.

Comment thread src/flow.py
else:
edge_index_dict[EDGE_PW] = torch.empty(
2, 0, dtype=torch.long, device=device
# Protein-water and water-protein edges get the extra pass; water-water does not.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why does ww edge not get the rescue? also I see that the isolate_axis is different for pw vs wp, might worth a comment in __init__ to explain this asymmetry, as these two fallbacks are really to ensure that all waters always have neighbors to pass or receive messages.

Comment thread scripts/inference.py
k_ww=config.get("k_ww") or 16,
cutoff=config.get("cutoff", 8.0),
max_neighbors=config.get("max_neighbors", 256),
dynamic_edge_policy=config.get("dynamic_edge_policy", "radius"),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

presumably at this point you are only dealing with new training runs that used "radius"? otherwise the old config presumably doesn't save this info, and you don't have cli args in the inference script to overwrite this.

Comment thread src/flow.py
Build the edge set for one batch under the active policy.

PP edges are read from the dataset (cached at preprocessing time).
PP and PW edges are read from the dataset when cached at preprocessing

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I thought you only cache PP edges because water position changes? Why adding PW cache reading here, do you ever save it as cache?

Comment thread src/flow.py
n_update_gvps: int = 2,
vector_gate: bool = True,
water_input_dim: int = 16, # 1 hot with oxygen, same as encoder
cutoff: float = 8.0,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

it looks like this cutoff value is intended to be reused as graph_cutoff when doing uniform ball sampling, which at line 910 (outside diff) also defaults to 8.0 if the cutoff here is not set. Consider extract this as constant to enforce consistency over hard coded value. Also, is this in any way related to the constant RBF_CUTOFF which also happens to be 8.0? If the radial edge cutoff > RBF_CUTOFF, do you get unintended zeroing of features?

Comment thread tests/test_flow.py
dynamic_edge_policy="knn_if_isolated",
knn_fallback_k=knn_fallback_k,
)
edge_index = updater.build_edges(data)[EDGE_PW]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

consider also have a test for the fallback of WP edge

def test_inference_build_model_from_config_replays_recorded_edge_policy(device):
"""Every recorded config carries "auto". Replaying one must build a model,
not raise, and must land on the radius path those runs actually used."""
config = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

do you not need to pass sampling_strategy here and check rescue? if training does scaled_gaussian and "auto" resolves to knn_if_isolated, but inference is radius, is this ok?

Comment thread tests/test_forward.py
Comment on lines -186 to -187
k_pw=8, # keep <= n_water_per
k_ww=8, # keep <= n_water_per

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

there are a few more traces of these k_pw and k_ww arguments in this test file (lines 266, 334, and 379). If you didn't mean to test explicitly on knn as the edge policy, consider clean those up as well.

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.

3 participants