Skip to content

UC capital domain check: np.isclose admits a band around the sentinel, so the top of the undefined interval is still accepted (and read as absence) #833

Description

@vahid-ahmadi

Follow-up to a verification finding on #829 that was posted shortly before merge, so it is now on main rather than in an open PR. Small, and not urgent on current vintages — filing so it does not live only in a comment on a closed PR.

What

The UC capital domain check accepts a narrow band around the sentinel as "exactly the sentinel", so the top sliver of the interval that #829 set out to close is still admitted.

packages/microcosm-build/src/microcosm/build/uk_runtime/battery_bindings.py:393-398 (at bc43f2f0):

out_of_domain = np.isfinite(capital) & ~(
    np.isclose(capital, sentinel) | (capital >= 0.0)
)

and the stage-entry check at packages/microcosm-build/src/microcosm/build/uk_runtime/uc_capital_coherence.py:121-123:

valid_domain = np.isfinite(capital) & (
    np.isclose(capital, UC_CAPITAL_UNAVAILABLE) | (capital >= 0.0)
)

np.isclose with default tolerances (atol=1e-8, rtol=1e-5) treats anything within roughly [-1.00001, -0.99999] as equal to -1. So a corrupted value such as -1.000005 passes both layers.

Why it matters slightly more than its width suggests

The direction of the leak is the unhelpful one. A value in that band is not merely tolerated — it is classified as the sentinel, so it is then read as "capital unavailable" and routed to the residual household proxy, and the >= 0 donor filter excludes it. A corrupted number is silently reinterpreted as a declared absence rather than refused.

It also contradicts the code's own stated contract. The refusal message says values must be "exactly the named unavailable sentinel or nonnegative", and the comment above out_of_domain says the open interval is "the one region the contract does not define" — but the implementation defines a small part of it after all.

Suggested fix

Exact comparison, in both places:

np.isfinite(capital) & ((capital == sentinel) | (capital >= 0.0))

UC_CAPITAL_UNAVAILABLE = -1.0 is a fixed literal rather than a computed float, and every producer either writes that literal or writes a non-negative amount, so exact equality is safe here in a way it usually would not be for floats. A tolerance far tighter than the amount grain would also do, but exact matches the message.

Not urgent

The I2 vintage audit found TOTCAPB4 fully populated (18,850/18,850), so zero sentinel rows exist on the current build and nothing in the band occurs today. This is a fence-tightening for a future vintage that does carry sentinels — the same reason the domain check was added rather than deferred.

Context

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions