99
1010from activitysim .core import chunk , interaction_simulate , logit , tracing , util , workflow
1111from activitysim .core .configuration .base import ComputeSettings
12- from activitysim .core .simulate import set_skim_wrapper_targets
1312from activitysim .core .exceptions import SegmentedSpecificationError
13+ from activitysim .core .logit import AltsContext
14+ from activitysim .core .simulate import set_skim_wrapper_targets
1415
1516logger = logging .getLogger (__name__ )
1617
@@ -34,6 +35,7 @@ def _interaction_sample_simulate(
3435 * ,
3536 chunk_sizer : chunk .ChunkSizer ,
3637 compute_settings : ComputeSettings | None = None ,
38+ alts_context : AltsContext | None = None ,
3739):
3840 """
3941 Run a MNL simulation in the situation in which alternatives must
@@ -220,9 +222,6 @@ def _interaction_sample_simulate(
220222 )
221223 chunk_sizer .log_df (trace_label , "interaction_utilities" , interaction_utilities )
222224
223- del interaction_df
224- chunk_sizer .log_df (trace_label , "interaction_df" , None )
225-
226225 if have_trace_targets :
227226 state .tracing .trace_interaction_eval_results (
228227 trace_eval_results ,
@@ -264,19 +263,29 @@ def _interaction_sample_simulate(
264263
265264 # insert the zero-prob utilities to pad each alternative set to same size
266265 padded_utilities = np .insert (interaction_utilities .utility .values , inserts , - 999 )
266+ padded_alt_nrs = np .insert (interaction_df [choice_column ], inserts , - 999 )
267267 chunk_sizer .log_df (trace_label , "padded_utilities" , padded_utilities )
268- del inserts
269268
270- del interaction_utilities
271- chunk_sizer .log_df (trace_label , "interaction_utilities" , None )
269+ del interaction_df
270+ chunk_sizer .log_df (trace_label , "interaction_df" , None )
271+
272+ del inserts
272273
273274 # reshape to array with one row per chooser, one column per alternative
274275 padded_utilities = padded_utilities .reshape (- 1 , max_sample_count )
276+ padded_alt_nrs = padded_alt_nrs .reshape (- 1 , max_sample_count )
275277
276278 # convert to a dataframe with one row per chooser and one column per alternative
277279 utilities_df = pd .DataFrame (padded_utilities , index = choosers .index )
278280 chunk_sizer .log_df (trace_label , "utilities_df" , utilities_df )
279281
282+ # alt_nrs_df has columns for each alt in the choice set, with values indicating which alt_id
283+ # they correspond to (as opposed to the 0-n index implied by the column number).
284+ if alts_context is not None :
285+ alt_nrs_df = pd .DataFrame (padded_alt_nrs , index = choosers .index )
286+ else :
287+ alt_nrs_df = None # if we don't provide the number of dense alternatives, assume that we'll use the old approach
288+
280289 del padded_utilities
281290 chunk_sizer .log_df (trace_label , "padded_utilities" , None )
282291
@@ -320,7 +329,12 @@ def _interaction_sample_simulate(
320329 # positions is series with the chosen alternative represented as a column index in utilities_df
321330 # which is an integer between zero and num alternatives in the alternative sample
322331 positions , rands = logit .make_choices_utility_based (
323- state , utilities_df , trace_label = trace_label , trace_choosers = choosers
332+ state ,
333+ utilities_df ,
334+ trace_label = trace_label ,
335+ trace_choosers = choosers ,
336+ alts_context = alts_context ,
337+ alt_nrs_df = alt_nrs_df ,
324338 )
325339
326340 del utilities_df
@@ -451,6 +465,7 @@ def interaction_sample_simulate(
451465 skip_choice = False ,
452466 explicit_chunk_size = 0 ,
453467 * ,
468+ alts_context : AltsContext | None = None ,
454469 compute_settings : ComputeSettings | None = None ,
455470):
456471 """
@@ -496,6 +511,12 @@ def interaction_sample_simulate(
496511 explicit_chunk_size : float, optional
497512 If > 0, specifies the chunk size to use when chunking the interaction
498513 simulation. If < 1, specifies the fraction of the total number of choosers.
514+ alts_context: AltsContext, optional
515+ Representation of the full alternatives domain (min and max alternative id)
516+ in the absence of sampling.
517+ This is used with EET simulation to ensure consistent random numbers across the whole alternative set
518+ ( as the sampled set may change between base and project). When not provided,
519+ EET with integer-coded choice ids will raise an error.
499520
500521 Returns
501522 -------
@@ -517,6 +538,18 @@ def interaction_sample_simulate(
517538 trace_label = tracing .extend_trace_label (trace_label , "interaction_sample_simulate" )
518539 chunk_tag = chunk_tag or trace_label
519540
541+ if state .settings .use_explicit_error_terms :
542+ choice_ids_are_int = pd .api .types .is_integer_dtype (alternatives [choice_column ])
543+ if alts_context is None and choice_ids_are_int :
544+ raise ValueError (
545+ "alts_context is required for interaction_sample_simulate when "
546+ "use_explicit_error_terms is True and choice_column is integer-coded"
547+ )
548+ if alts_context is not None and not choice_ids_are_int :
549+ raise ValueError (
550+ "alts_context can only be used with integer-coded choice_column values"
551+ )
552+
520553 result_list = []
521554 for (
522555 i ,
@@ -551,6 +584,7 @@ def interaction_sample_simulate(
551584 skip_choice ,
552585 chunk_sizer = chunk_sizer ,
553586 compute_settings = compute_settings ,
587+ alts_context = alts_context ,
554588 )
555589
556590 result_list .append (choices )
0 commit comments