Skip to content

Fix #1187: Allow cyclical subexpressions with 'constant over dt' flag#1782

Open
nishantraghuvanshi wants to merge 6 commits intobrian-team:masterfrom
nishantraghuvanshi:fix/issue-1187-cyclical-subexpressions
Open

Fix #1187: Allow cyclical subexpressions with 'constant over dt' flag#1782
nishantraghuvanshi wants to merge 6 commits intobrian-team:masterfrom
nishantraghuvanshi:fix/issue-1187-cyclical-subexpressions

Conversation

@nishantraghuvanshi
Copy link
Copy Markdown

@nishantraghuvanshi nishantraghuvanshi commented Mar 3, 2026

nishantraghuvanshi and others added 4 commits March 3, 2026 15:41
…r dt' flag

- Exclude 'constant over dt' subexpressions from cycle detection
- Add test_cyclical_subexpressions() to verify the fix
- These subexpressions are evaluated once per timestep, not recursively
(basically a dummy commit to trigger test suite)
  Previous implementation incorrectly set 'constant over dt' subexpressions
  to have no dependencies, which broke proper ordering and caused test failures.

  Changes:
  1. Keep all dependencies for accurate ordering
  2. Separate regular and 'constant over dt' subexpressions
  3. Sort regular ones with strict topsort (no cycles allowed)
  4. Sort 'constant over dt' ones with cycle-tolerant algorithm
  5. Combine the two sorted lists

  Also fixes test_namespace_warnings test isolation issue by filtering
  to only count resolution_conflict warnings, making the test robust
  to garbage collection timing differences.

  Fixes:
  - test_constant_subexpression_order failure
  - test_namespace_warnings test isolation issue
  - Maintains test_cyclical_subexpressions functionality
@mstimberg
Copy link
Copy Markdown
Member

Hi @nishantraghuvanshi Thanks for the PR update. Could you please briefly comment on why your last changes were necessary and give a short description of them? Thanks!

@nishantraghuvanshi
Copy link
Copy Markdown
Author

Hi @mstimberg,

The last commit fixes a subexpression ordering issue caused by the initial implementation.

Problem: Setting "constant over dt" subexpressions to have no dependencies broke their ordering.

Fix: Now I:

  • Keep all dependencies for proper ordering
  • Sort regular subexpressions with strict topsort
  • Sort "constant over dt" subexpressions with cycle-tolerant algorithm
  • Combine both sorted lists

This allows cycles for "constant over dt" while maintaining correct ordering. Also fixed a test isolation issue.
Do I need to make any changes to the approach?

@mstimberg
Copy link
Copy Markdown
Member

[Sorry, I meant to send this last week, but it was still sitting in one of my browser tabs
This looks pretty good to me. I'm not quite ready to merge it yet, since this is one of these ungrateful issues where we have to spend a lot of time looking into details even though they affect almost no code in practice. What I am wondering is whether this changes anything at all for the order of operations in the no-cycle case, e.g. if there are dependencies between the "constant over dt" and "classical" subexpressions. It would be nice to test this with a few examples to be sure, and also to document this once and for all with some clear rules for users to reason about how and when these subexpressions are calculated.

  Fixed issue where subexpression ordering broke cross-dependencies
  between regular and 'constant over dt' subexpressions.

  The old code separated and sorted the two groups independently, then
  combined them as [all regular] + [all constant], which broke dependencies
  like: s1(constant) -> s2(regular) -> s3(constant).

  The fix sorts all subexpressions together, then checks if cycles only
  involve 'constant over dt' subexpressions (allowed) or regular ones (error).

  This preserves old behavior for non-cyclical cases while allowing
  cyclical 'constant over dt' subexpressions as intended in issue brian-team#1187.
@nishantraghuvanshi
Copy link
Copy Markdown
Author

Hi @mstimberg,

Thank you for the careful review. You were absolutely right to be concerned about the order of operations.

The Bug

The current implementation separated subexpressions into two groups and sorted them independently:

  • Regular subexpressions sorted together
  • "Constant over dt" subexpressions sorted together
  • Then combined as: [all regular] + [all constant]

This breaks cross-dependencies. Example:

s1 = v + 1 : 1 (constant over dt)
s2 = s1 * 2 : 1  (regular)
s3 = s2 + 5 : 1 (constant over dt)

Old code order: s2 → s1 → s3 (this would cause issues)
Correct order: s1 → s2 → s3

The problem: when s3 is computed in SubexpressionUpdater, it needs s2 (which is inlined). But s2 references s1, which is found as a parameter in the model with its OLD value instead of the newly computed value.

The Fix

I've rewritten _sort_subexpressions() to:

  1. Sort ALL subexpressions together (preserves dependencies)
  2. If there's a cycle, check if it ONLY involves "constant over dt" subexpressions
    • If yes → allow with cycle-tolerant sort
    • If no → raise ValueError

This preserves the old behavior for non-cyclical cases while still allowing cyclical "constant over dt" subexpressions.
Do you think this approach is feasible?

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.

Allow cyclical subexpressions if marked "constant over dt"

2 participants