Skip to content

Commit 66cdac9

Browse files
author
Jan Zill
committed
consistently treat utilities that would lead to zero choices in MC simulation
1 parent e102a9e commit 66cdac9

9 files changed

Lines changed: 69 additions & 28 deletions

activitysim/abm/models/joint_tour_participation.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -213,19 +213,16 @@ def participants_chooser(
213213
)
214214
# anybody with probability > 0 is forced to join the joint tour
215215
if state.settings.use_explicit_error_terms:
216-
# need "is valid choice", which requires minimum probability. This depends
217-
# on all other utilities, and we need to make sure that we set values such that we
218-
# certainly choose those with non-zero values, and do not choose other ones. Let's
219-
# use -999 as zero prob choice and 10 as definitive choice (based on diff and std gumbel).
220-
probs_from_utils = logit.utils_to_probs(state, probs_or_utils)
216+
# need "is valid choice" such that we certainly choose those with non-zero values,
217+
# and do not choose others. Let's use 3.0 as large value here.
221218
probs_or_utils[choice_col] = np.where(
222-
probs_from_utils[choice_col] > 0, 10, -999
219+
probs_or_utils[choice_col] > logit.UTIL_MIN, 3.0, logit.UTIL_UNAVAILABLE
223220
)
224221
non_choice_col = [
225222
col for col in probs_or_utils.columns if col != choice_col
226223
][0]
227224
probs_or_utils[non_choice_col] = np.where(
228-
probs_or_utils[choice_col] == -999, 10, -999
225+
probs_or_utils[choice_col] <= logit.UTIL_MIN, 3.0, logit.UTIL_UNAVAILABLE
229226
)
230227
else:
231228
probs_or_utils[choice_col] = np.where(

activitysim/abm/models/trip_departure_choice.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ def choose_tour_leg_pattern(
314314
chunk_sizer.log_df(trace_label, "sample_counts", None)
315315

316316
# insert the zero-prob utilities to pad each alternative set to same size
317-
padded_utilities = np.insert(interaction_utilities.utility.values, inserts, -999999)
317+
padded_utilities = np.insert(interaction_utilities.utility.values, inserts, -999)
318318
del inserts
319319

320320
del interaction_utilities
@@ -339,6 +339,9 @@ def choose_tour_leg_pattern(
339339
)
340340

341341
if state.settings.use_explicit_error_terms:
342+
utilities_df = logit.validate_utils(
343+
state, utilities_df, trace_label=trace_label, trace_choosers=trip_segment
344+
)
342345
# make choices
343346
# positions is series with the chosen alternative represented as a column index in probs
344347
# which is an integer between zero and num alternatives in the alternative sample

activitysim/abm/models/util/cdap.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,8 @@ def household_activity_choices(
996996
utils = utils.add(joint_tour_utils)
997997

998998
if state.settings.use_explicit_error_terms:
999+
utils = logit.validate_utils(state, utils, trace_label=trace_label)
1000+
9991001
idx_choices, rands = logit.make_choices_utility_based(
10001002
state, utils, trace_label=trace_label
10011003
)

activitysim/core/interaction_sample.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ def make_sample_choices_utility_based(
4343
assert isinstance(alternatives, pd.DataFrame)
4444
assert len(alternatives) == alternative_count
4545

46+
if allow_zero_probs:
47+
zero_probs = (
48+
utilities.sum(axis=1)
49+
<= utilities.shape[1] * logit.UTIL_UNAVAILABLE
50+
)
51+
if zero_probs.all():
52+
return pd.DataFrame(
53+
columns=[alt_col_name, "rand", "prob", choosers.index.name]
54+
)
55+
if zero_probs.any():
56+
# remove from sample
57+
utilities = utilities[~zero_probs]
58+
choosers = choosers[~zero_probs]
59+
4660
utils_array = utilities.to_numpy()
4761
chunk_sizer.log_df(trace_label, "utils_array", utils_array)
4862
chosen_destinations = []
@@ -556,6 +570,14 @@ def _interaction_sample(
556570
return choices_df
557571

558572
if use_eet:
573+
utilities = logit.validate_utils(
574+
state,
575+
utilities,
576+
allow_zero_probs=allow_zero_probs,
577+
trace_label=trace_label,
578+
trace_choosers=choosers,
579+
)
580+
559581
choices_df = make_sample_choices_utility_based(
560582
state,
561583
choosers,

activitysim/core/interaction_sample_simulate.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -262,14 +262,7 @@ def _interaction_sample_simulate(
262262
chunk_sizer.log_df(trace_label, "sample_counts", None)
263263

264264
# insert the zero-prob utilities to pad each alternative set to same size
265-
# -999 is not small enough for EET, there are edge cases where alternatives
266-
# corresponding to padded utilities are chosen. TODO: proper zero-prob handling.
267-
zero_probability_util = (
268-
-999999
269-
) # min(interaction_utilities.utility.min().min() - 100, -999)
270-
padded_utilities = np.insert(
271-
interaction_utilities.utility.values, inserts, zero_probability_util
272-
)
265+
padded_utilities = np.insert(interaction_utilities.utility.values, inserts, -999)
273266
chunk_sizer.log_df(trace_label, "padded_utilities", padded_utilities)
274267
del inserts
275268

activitysim/core/interaction_simulate.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,9 @@ def _interaction_simulate(
883883
state.tracing.dump_df(DUMP, utilities, trace_label, "utilities")
884884

885885
if state.settings.use_explicit_error_terms:
886+
utilities = logit.validate_utils(
887+
state, utilities, trace_label=trace_label, trace_choosers=choosers
888+
)
886889
positions, rands = logit.make_choices_utility_based(
887890
state, utilities, trace_label=trace_label, trace_choosers=choosers
888891
)

activitysim/core/pathbuilder.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -991,45 +991,58 @@ def build_virtual_path(
991991
utilities_df.index = orig.index
992992

993993
with memo("#TVPB build_virtual_path make_choices"):
994-
probs = logit.utils_to_probs(
995-
self.network_los.state,
996-
utilities_df,
997-
allow_zero_probs=True,
998-
trace_label=trace_label,
999-
overflow_protection=False,
1000-
)
1001-
chunk_sizer.log_df(trace_label, "probs", probs)
1002-
1003994
if trace:
995+
probs = logit.utils_to_probs(
996+
self.network_los.state,
997+
utilities_df,
998+
allow_zero_probs=True,
999+
trace_label=trace_label,
1000+
overflow_protection=False,
1001+
)
1002+
chunk_sizer.log_df(trace_label, "probs", probs)
1003+
10041004
choices = override_choices
10051005

10061006
utilities_df["choices"] = choices
10071007
self.trace_df(utilities_df, trace_label, "utilities_df")
10081008

10091009
probs["choices"] = choices
10101010
self.trace_df(probs, trace_label, "probs")
1011+
del probs
1012+
chunk_sizer.log_df(trace_label, "probs", None)
10111013
else:
10121014
if self.network_los.state.settings.use_explicit_error_terms:
1015+
utilities_df = logit.validate_utils(
1016+
self.network_los.state, utilities_df, allow_zero_probs=True, trace_label=trace_label
1017+
)
10131018
choices, rands = logit.make_choices_utility_based(
10141019
self.network_los.state,
10151020
utilities_df,
1016-
allow_bad_probs=True,
10171021
trace_label=trace_label,
10181022
)
10191023
else:
1024+
probs = logit.utils_to_probs(
1025+
self.network_los.state,
1026+
utilities_df,
1027+
allow_zero_probs=True,
1028+
trace_label=trace_label,
1029+
overflow_protection=False,
1030+
)
1031+
chunk_sizer.log_df(trace_label, "probs", probs)
1032+
10201033
choices, rands = logit.make_choices(
10211034
self.network_los.state,
10221035
probs,
10231036
allow_bad_probs=True,
10241037
trace_label=trace_label,
10251038
)
1039+
del probs
1040+
chunk_sizer.log_df(trace_label, "probs", None)
10261041

10271042
chunk_sizer.log_df(trace_label, "rands", rands)
10281043
del rands
10291044
chunk_sizer.log_df(trace_label, "rands", None)
10301045

1031-
del probs
1032-
chunk_sizer.log_df(trace_label, "probs", None)
10331046

10341047
# we need to get path_set, btap, atap from path_df row with same seq and path_num
10351048
# drop seq join column, but keep path_num of choice to override_choices when tracing

activitysim/core/simulate.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,8 @@ def eval_mnl(
12901290
)
12911291

12921292
if state.settings.use_explicit_error_terms:
1293+
utilities = logit.validate_utils(state, utilities, trace_label=trace_label, trace_choosers=choosers)
1294+
12931295
if custom_chooser:
12941296
choices, rands = custom_chooser(
12951297
state, utilities, choosers, spec, trace_label
@@ -1427,6 +1429,11 @@ def eval_nl(
14271429
)
14281430

14291431
if state.settings.use_explicit_error_terms:
1432+
# TODO-EET: Nested utility zero choice probability
1433+
raw_utilities = logit.validate_utils(
1434+
state, raw_utilities, allow_zero_probs=True, trace_label=trace_label
1435+
)
1436+
14301437
# utilities of leaves and nests
14311438
nested_utilities = compute_nested_utilities(raw_utilities, nest_spec)
14321439
chunk_sizer.log_df(trace_label, "nested_utilities", nested_utilities)

activitysim/core/test/test_logit.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def utilities(choosers, spec, test_data):
6969
columns=test_data["probabilities"].columns,
7070
)
7171

72+
# TODO-EET: Add tests here!
7273

7374
def test_utils_to_probs(utilities, test_data):
7475
state = workflow.State().default_settings()

0 commit comments

Comments
 (0)