Describe the bug
Sharrow returns float32 utilities. In trip destination, interaction_sample_simulate calls logit.utils_to_probs with allow_zero_probs=True, overflow_protection=False. Finite float32 utilities above approximately 88 overflow during exponentiation, producing infinite exponentiated utilities. The corresponding float64 values complete.
Confirmed at ActivitySim commit 61078c7198b46c4d4ff336caa7678bd814b6fa82 (1.5.1), with Sharrow 2.16.2 and Numba 0.66.0 on Linux. The minimal probability-conversion reproducer does not need Sharrow, regional data, or a simulation.
To reproduce
Run the following in the canonical ActivitySim environment. The temporary replacement of report_bad_choices captures the error without requiring a simulation State or tracing setup; it does not change probability conversion and is restored afterward.
"""Minimal #262 reproducer; run with canonical ActivitySim on PYTHONPATH.
No regional data or simulation is needed. Capturing report_bad_choices only
replaces error reporting; the actual canonical probability conversion runs.
"""
from __future__ import annotations
import json
import numpy as np
import pandas as pd
def replay():
from activitysim.core import logit
class ProbabilityFailure(Exception):
pass
def report(state, bad_row_map, frame, **kwargs):
raise ProbabilityFailure(kwargs["msg"])
original = logit.report_bad_choices
logit.report_bad_choices = report
records = {}
try:
for dtype in (np.float32, np.float64):
utilities = pd.DataFrame([[200.0, 199.0, -999.0]], dtype=dtype)
try:
with np.errstate(over="ignore"):
probabilities = logit.utils_to_probs(
None, utilities, allow_zero_probs=True, overflow_protection=False
)
records[np.dtype(dtype).name] = {
"status": "completed",
"probabilities": probabilities.to_numpy().tolist(),
}
except ProbabilityFailure as error:
records[np.dtype(dtype).name] = {"status": "failed", "error": str(error)}
empty = logit.utils_to_probs(
None,
pd.DataFrame([[-999.0, -999.0]], dtype=np.float32),
allow_zero_probs=True,
overflow_protection=False,
)
records["all_unavailable"] = empty.to_numpy().tolist()
finally:
logit.report_bad_choices = original
return records
if __name__ == "__main__":
print(json.dumps(replay(), indent=2))
Actual: float32 reports infinite exponentiated utilities; float64 returns [0.7310585786300049, 0.26894142136999516, 0.0]. The all-unavailable control returns [0.0, 0.0].
Expected behavior
Finite inputs [200, 199, -999] should yield the normalized probabilities above, while all-unavailable rows must retain zero probabilities. Simply enabling row-max shifting must account for that all-unavailable contract.
Additional context and candidate fix
A saved trip-destination failure matrix contained 81 rows with entirely finite utilities (maximum 215.122). Replaying the same already-rounded values in float64 resolves all 81 failures. This identifies exponentiation overflow, not an established Sharrow expression-semantic error.
A narrow benchmark patch immediately after utils_arr = utils.values was:
if allow_zero_probs and not exponentiated and utils_arr.dtype == np.float32:
utils_arr = utils_arr.astype(np.float64)
With this patch, cold and warm full-population Sharrow runs completed and their four exported tables matched byte-for-byte. This is a candidate for review, not a general solution for arbitrary extreme utilities: float64 can also overflow. A more robust normalization that preserves all-unavailable semantics may be preferable.
The benchmark did not clip utilities or change expressions, coefficients, or sentinels. Reproducer, patch provenance, and results: https://github.com/urbansim/activitysim-torch/pull/271 . Local investigation: https://github.com/urbansim/activitysim-torch/issues/262 .
Describe the bug
Sharrow returns float32 utilities. In trip destination,
interaction_sample_simulatecallslogit.utils_to_probswithallow_zero_probs=True, overflow_protection=False. Finite float32 utilities above approximately 88 overflow during exponentiation, producinginfinite exponentiated utilities. The corresponding float64 values complete.Confirmed at ActivitySim commit
61078c7198b46c4d4ff336caa7678bd814b6fa82(1.5.1), with Sharrow 2.16.2 and Numba 0.66.0 on Linux. The minimal probability-conversion reproducer does not need Sharrow, regional data, or a simulation.To reproduce
Run the following in the canonical ActivitySim environment. The temporary replacement of
report_bad_choicescaptures the error without requiring a simulation State or tracing setup; it does not change probability conversion and is restored afterward.Actual: float32 reports
infinite exponentiated utilities; float64 returns[0.7310585786300049, 0.26894142136999516, 0.0]. The all-unavailable control returns[0.0, 0.0].Expected behavior
Finite inputs
[200, 199, -999]should yield the normalized probabilities above, while all-unavailable rows must retain zero probabilities. Simply enabling row-max shifting must account for that all-unavailable contract.Additional context and candidate fix
A saved trip-destination failure matrix contained 81 rows with entirely finite utilities (maximum 215.122). Replaying the same already-rounded values in float64 resolves all 81 failures. This identifies exponentiation overflow, not an established Sharrow expression-semantic error.
A narrow benchmark patch immediately after
utils_arr = utils.valueswas:With this patch, cold and warm full-population Sharrow runs completed and their four exported tables matched byte-for-byte. This is a candidate for review, not a general solution for arbitrary extreme utilities: float64 can also overflow. A more robust normalization that preserves all-unavailable semantics may be preferable.
The benchmark did not clip utilities or change expressions, coefficients, or sentinels. Reproducer, patch provenance, and results: https://github.com/urbansim/activitysim-torch/pull/271 . Local investigation: https://github.com/urbansim/activitysim-torch/issues/262 .