Skip to content

[ty] Remove some nondeterminism in constraint set tests#22064

Merged
dcreager merged 3 commits intomainfrom
dcreager/nondeterminism
Dec 19, 2025
Merged

[ty] Remove some nondeterminism in constraint set tests#22064
dcreager merged 3 commits intomainfrom
dcreager/nondeterminism

Conversation

@dcreager
Copy link
Member

@dcreager dcreager commented Dec 18, 2025

We're seeing a lot of nondeterminism in the ecosystem tests at the moment, which started (or at least got worse) once Callable inference landed.

This PR attempts to remove this nondeterminism. We recently (#21983) added a source_order field to BDD nodes, which tracks when their constraint was added to the BDD. Since we build up constraints based on the order that they appear in the underlying source, that gives us a stable ordering even though we use an arbitrary salsa-derived ordering for the BDD variables.

The issue (at least for some of the flakiness) is that we add "derived" constraints when walking a BDD tree, and those derived constraints inherit or borrow the source_order of the "real" constraint that implied them. That means we can get multiple constraints in our specialization that all have the same source_order. If we're not careful, those "tied" constraints can be ordered arbitrarily.

The fix requires three four several steps:

  • When starting to construct a sequent map (the data structure that stores the derived constraints), we first sort all of the "real" constraints by their source_order. That ensures that we insert things into the sequent map in a stable order.
  • During sequent map construction, derived facts are discovered by a deterministic process applied to constraints in a (now) stable order. So derived facts are now also inserted in a stable order.
  • We update the fields of SequentMap to use FxOrderSet instead of FxHashSet, so that we retain that stable insertion order.
  • When walking BDD paths when constructing a specialization, we were already sorting the constraints by their source_order. However, we were not considering that we might get derived constraints, and therefore constraints with "ties". Because of that, we need to make sure to use a stable sort, that retains the insertion order for those ties.

All together, this...should...fix the nondeterminism. (Unfortunately, I haven't been able to effectively test this, since I haven't been able to coerce local tests to flop into the other order that we sometimes see in CI.)

@dcreager dcreager added internal An internal refactor or improvement ty Multi-file analysis & type inference labels Dec 18, 2025
/// - `C → D`: This indicates that `C` on its own is enough to imply `D`. Any path that assumes `C`
/// holds but `D` does _not_ is impossible and can be pruned.
#[derive(Debug, Default, Eq, PartialEq, get_size2::GetSize, salsa::Update)]
struct SequentMap<'db> {
Copy link
Member Author

Choose a reason for hiding this comment

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

There are other FxHashMaps in here, but they're only used for existence checks; we never iterate over them. The ones changed below are the only ones we iterate over, and where insertion order is therefore important.

@astral-sh-bot
Copy link

astral-sh-bot bot commented Dec 18, 2025

Diagnostic diff on typing conformance tests

No changes detected when running ty on typing conformance tests ✅

@astral-sh-bot
Copy link

astral-sh-bot bot commented Dec 18, 2025

mypy_primer results

Changes were detected when running on open source projects
Tanjun (https://github.com/FasterSpeeding/Tanjun)
- tanjun/dependencies/data.py:347:12: error[invalid-return-type] Return type does not match returned value: expected `_T@cached_inject`, found `_T@cached_inject | Coroutine[Any, Any, _T@cached_inject | Coroutine[Any, Any, _T@cached_inject]]`
+ tanjun/dependencies/data.py:347:12: error[invalid-return-type] Return type does not match returned value: expected `_T@cached_inject`, found `Coroutine[Any, Any, _T@cached_inject | Coroutine[Any, Any, _T@cached_inject]] | _T@cached_inject`

xarray (https://github.com/pydata/xarray)
- xarray/core/dataarray.py:5737:16: error[invalid-return-type] Return type does not match returned value: expected `T_Xarray@map_blocks`, found `T_Xarray@map_blocks | DataArray | Dataset`
+ xarray/core/dataarray.py:5737:16: error[invalid-return-type] Return type does not match returned value: expected `T_Xarray@map_blocks`, found `DataArray | Dataset`
- xarray/core/dataset.py:8866:16: error[invalid-return-type] Return type does not match returned value: expected `T_Xarray@map_blocks`, found `T_Xarray@map_blocks | DataArray | Dataset`
+ xarray/core/dataset.py:8866:16: error[invalid-return-type] Return type does not match returned value: expected `T_Xarray@map_blocks`, found `DataArray | Dataset`

static-frame (https://github.com/static-frame/static-frame)
- static_frame/core/bus.py:671:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemLocReduces[Bus[Any], object_]`, found `InterGetItemLocReduces[Bus[Any] | Top[Series[Any, Any]] | TypeBlocks | ... omitted 7 union elements, object_]`
- static_frame/core/bus.py:675:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[Bus[Any], object_]`, found `InterGetItemILocReduces[Bus[Any] | Top[Index[Any]] | TypeBlocks | ... omitted 7 union elements, generic[object]]`
+ static_frame/core/bus.py:671:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemLocReduces[Bus[Any], object_]`, found `InterGetItemLocReduces[Bus[Any] | Top[Index[Any]] | TypeBlocks | ... omitted 7 union elements, object_]`
+ static_frame/core/bus.py:675:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[Bus[Any], object_]`, found `InterGetItemILocReduces[Bus[Any] | Top[Index[Any]] | TypeBlocks | ... omitted 7 union elements, object_ | Self@iloc]`
- static_frame/core/series.py:772:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[Series[Any, Any], TVDtype@Series]`, found `InterGetItemILocReduces[Series[Any, Any] | Top[Index[Any]] | TypeBlocks | ... omitted 6 union elements, generic[object]]`
+ static_frame/core/series.py:772:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[Series[Any, Any], TVDtype@Series]`, found `InterGetItemILocReduces[Series[Any, Any] | TypeBlocks | Batch | ... omitted 6 union elements, generic[object]]`
- static_frame/core/series.py:4072:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[SeriesHE[Any, Any], TVDtype@SeriesHE]`, found `InterGetItemILocReduces[SeriesHE[Any, Any] | Top[Index[Any]] | TypeBlocks | ... omitted 7 union elements, generic[object]]`
+ static_frame/core/series.py:4072:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[SeriesHE[Any, Any], TVDtype@SeriesHE]`, found `InterGetItemILocReduces[SeriesHE[Any, Any] | TypeBlocks | Batch | ... omitted 7 union elements, generic[object]]`

pandas-stubs (https://github.com/pandas-dev/pandas-stubs)
- pandas-stubs/_typing.pyi:1221:16: warning[unused-ignore-comment] Unused blanket `type: ignore` directive
+ tests/frame/test_groupby.py:228:15: error[type-assertion-failure] Type `Series[Any]` does not match asserted type `Series[str | bytes | int | ... omitted 12 union elements]`
+ tests/frame/test_groupby.py:624:15: error[type-assertion-failure] Type `Series[Any]` does not match asserted type `Series[str | bytes | int | ... omitted 12 union elements]`
- tests/test_groupby.py:433:11: error[type-assertion-failure] Type `Series[Any]` does not match asserted type `Series[(str & Any) | (bytes & Any) | (int & Any) | ... omitted 12 union elements]`
+ tests/test_groupby.py:433:11: error[type-assertion-failure] Type `Series[Any]` does not match asserted type `Series[(Any & str) | (Any & bytes) | (Any & int) | ... omitted 12 union elements]`
- tests/test_resampler.py:394:11: error[type-assertion-failure] Type `Series[Any]` does not match asserted type `Series[(str & Any) | (bytes & Any) | (int & Any) | ... omitted 12 union elements]`
+ tests/test_resampler.py:394:11: error[type-assertion-failure] Type `Series[Any]` does not match asserted type `Series[(Any & str) | (Any & bytes) | (Any & int) | ... omitted 12 union elements]`
- Found 5079 diagnostics
+ Found 5080 diagnostics

No memory usage changes detected ✅

@carljm
Copy link
Contributor

carljm commented Dec 18, 2025

It's hard to tell if this fixes the issue, because if it doesn't, or if it does but it "solidifies" an ordering that doesn't match what we happen to get on the "pre" run, the results will look the same 😆

Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

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

Seems reasonable, if potentially expensive?

Guess there's no way to see if it worked other than land it?

@AlexWaygood
Copy link
Member

AlexWaygood commented Dec 18, 2025

And (regarding #22064 (comment)) we we already non-deterministic before the Callable PR landed, so the aim isn't even "fully deterministic again" here, it's "more deterministic than we are right now"

@dcreager
Copy link
Member Author

Seems reasonable, if potentially expensive?

I hope (and codspeed CI seems to agree) that it won't be too bad — the biggest change is sorting before starting sequent map construction, and my intuition is that we're doing enough work finding all of the sequents that the extra sorting step won't be a bottleneck.

@dcreager
Copy link
Member Author

more deterministic than we are right now

To be fair, where we are now is really quite non-deterministic 😅

@dcreager dcreager merged commit 5a2d3cd into main Dec 19, 2025
44 checks passed
@dcreager dcreager deleted the dcreager/nondeterminism branch December 19, 2025 00:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal An internal refactor or improvement ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments