activitysim icon indicating copy to clipboard operation
activitysim copied to clipboard

Estimation Enhancements

Open dhensle opened this issue 11 months ago • 9 comments
trafficstars

Estimation work as part of ActivitySim's Phase 9B development effort.

  • [x] Implement multiprocessing for estimation mode
  • [x] Implement destination choice sampling in estimation mode
  • [x] Change the formatting and data written to EDBs
  • [x] Update Larch integration to accept new file formats
  • [x] Functionality to quickly test different specifications
  • [x] Improved larch reporting on estimated models
  • [x] Adding "predict" functionality with estimated models in larch
  • [x] Unit testing for the above features
  • [x] Updated documentation

dhensle avatar Dec 17 '24 01:12 dhensle

Hi @dhensle, do you have an ETA for when this PR will be completed? Has it been decided which version will include this enhancement? I have been testing it with our Victoria ActivitySim implementation, so I have a vested interest in your PR. Thank you! :)

asiripanich avatar May 10 '25 11:05 asiripanich

Hi @asiripanich, thank you for your interest and testing! We are just putting the finishing touches on this and hope to have it pulled in by the end of the month.

Any feedback from you on the new features? Now's the time to get any changes you might want put in!

dhensle avatar May 14 '25 00:05 dhensle

Hi @asiripanich, thank you for your interest and testing! We are just putting the finishing touches on this and hope to have it pulled in by the end of the month.

Any feedback from you on the new features? Now's the time to get any changes you might want put in!

I have been able to get this enhanced estimation mode to work with our travel survey. The parquet EBDs are definitely a huge improvement over the CSVs. Thanks for the work!

A few comments:

  • I remember the CDAP estimation function wasn't working because my ActivitySim outputs were in Parquet format. I had to add a line to reset the index when reading in Parquet inputs; this is not an issue if you are reading in CSV files.
  • I feel like the specification files could be more compact if the coefficient values weren't separated into another file just to specify their constraint values. I understand this would require some work and planning, but how about adding a symbol (e.g., an asterisk *) in front of the coefficient values in the specification file if you want to fix their value? Creating a new label column and coefficient names feels unnecessary and just increases the number of input files that one has to manage, which means a higher chance of making errors. If the label field is required in the specification file, I think it could be automatically generated from the description field by converting the description to underscore case.

Example:

Description Expression M N H
Full-time worker alternative-specific constants ptype == 1 *0.885080091 0.531583624
Part-time worker alternative-specific constants ptype == 2 *-0.920808727 1.117988879
University student alternative-specific constants ptype == 3 1.898468936 -0.380144113

asiripanich avatar May 21 '25 00:05 asiripanich

Hi @dhensle

I tested the new estimation tool. Great work! I did notice a minor bug related to the file naming. When generating the EDB for school_location, for example, the tool creates a CSV file named school_location_school_location_coeffs.csv instead of school_location_coefficients.csv

For now, I’m manually renaming the file, but it might be a small bug worth checking in the naming logic. Thanks!

AmirSamimi-TfNSW avatar Jun 05 '25 20:06 AmirSamimi-TfNSW

Hi @dhensle

I tested the new estimation tool. Great work! I did notice a minor bug related to the file naming. When generating the EDB for school_location, for example, the tool creates a CSV file named school_location_school_location_coeffs.csv instead of school_location_coefficients.csv

For now, I’m manually renaming the file, but it might be a small bug worth checking in the naming logic. Thanks!

Hi @AmirSamimi-TfNSW, thanks for your testing!

The code should be writing the coefficients file with the file name specified in the model yaml settings file. Can you please check the COEFFICIENT setting in your school_location.yaml and see if this naming issue stems from that file? I do not see this issue in any of my outputs.

dhensle avatar Jun 11 '25 22:06 dhensle

Hi @asiripanich, thank you for your interest and testing! We are just putting the finishing touches on this and hope to have it pulled in by the end of the month. Any feedback from you on the new features? Now's the time to get any changes you might want put in!

I have been able to get this enhanced estimation mode to work with our travel survey. The parquet EBDs are definitely a huge improvement over the CSVs. Thanks for the work!

A few comments:

  • I remember the CDAP estimation function wasn't working because my ActivitySim outputs were in Parquet format. I had to add a line to reset the index when reading in Parquet inputs; this is not an issue if you are reading in CSV files.
  • I feel like the specification files could be more compact if the coefficient values weren't separated into another file just to specify their constraint values. I understand this would require some work and planning, but how about adding a symbol (e.g., an asterisk *) in front of the coefficient values in the specification file if you want to fix their value? Creating a new label column and coefficient names feels unnecessary and just increases the number of input files that one has to manage, which means a higher chance of making errors. If the label field is required in the specification file, I think it could be automatically generated from the description field by converting the description to underscore case.

Example:

Description Expression M N H Full-time worker alternative-specific constants ptype == 1 *0.885080091 0.531583624 Part-time worker alternative-specific constants ptype == 2 *-0.920808727 1.117988879 University student alternative-specific constants ptype == 3 1.898468936 -0.380144113

Hi @asiripanich glad the new EDBs have been working for you! In response to your comments:

  • CDAP is unique in that it also will read the person and household files. I have updated the parquet read code that exists in this model to handle that indexing issue. Thanks for flagging.
  • The decision to move coefficients into a separate file was made when estimation mode was first implemented. I agree that the coefficient values are much harder to see with them being stored in a different file. However, having coefficient names that can be used and estimated across multiple utility terms is the primary reason this change was done. Take for example the cost and in-vehicle time coefficients in mode choice --- they are used numerous times across modes and utility terms. Specifying the coefficient values separately from the spec makes this much easier to see and much easier to estimate. If you would like to have a broader discussion around how we handle coefficient values in ActivitySim configs, I would welcome that discussion in a separate issue as it is outside the scope of this PR. (Perhaps we just need to create a little utility that will output a spec with coefficient values applied for human-readability?)

Thanks for your comments!

dhensle avatar Jun 11 '25 22:06 dhensle

Thanks @dhensle for your response.

I ran into an issue today while trying to add a new utility term to the tour mode choice spec of my tour mode choice EDB. This might be a bug or something that needs to be explained a bit more in the documentation. (However, I didn't have any issues modifying or adding new terms to the auto_ownership model. Despite this minor issue, I'm loving this new capability!)

When I tried to add a new line to my tour mode choice spec file, I kept getting this error:

modelname = "tour_mode_choice"

from activitysim.estimation.larch import component_model

model, data = component_model(
    modelname,
    edb_directory=f"output/estimation_data_bundle/{modelname}/",
    return_data=True,
)

> loading from output/estimation_data_bundle/tour_mode_choice/tour_mode_choice_coefficients.csv
> loading from output/estimation_data_bundle/tour_mode_choice/tour_mode_choice_coefficients_template.csv
> loading spec from output/estimation_data_bundle/tour_mode_choice/tour_mode_choice_SPEC.csv
> loading from output/estimation_data_bundle/tour_mode_choice/tour_mode_choice_values_combined.parquet
> unable to rewrite 'util_test' to itself

I tested this using example_estimation and 17_tour_mode_choice.ipynb from this PR.

I added these following lines:

tour_mode_choice_SPEC.csv

util_test,Drive alone not available for escort tours,1,coef_test,,,,,,,,,,,,,,,,,,,,

tour_mode_choice_coefficients_template.csv

coef_test,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_school_univ,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_school_univ,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork

tour_mode_choice_coefficients.csv

coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,0,F
coef_test_school_univ,0,F

Any advice on what I might be doing wrong would be greatly appreciated. šŸ˜€

See full error

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:942, in DataTree.get_expr(self, expression, engine, allow_native, dtype, with_coords, parser)
    941     else:
--> [942](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:942)         raise KeyError
    943 except (KeyError, IndexError):

KeyError: 

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:958, in DataTree.get_expr(self, expression, engine, allow_native, dtype, with_coords, parser)
    956 try:
    957     result = DataArray(
--> [958](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:958)         ne.evaluate(expression, local_dict=CachedTree(self)),
    959     )
    960 except Exception:

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/numexpr/necompiler.py:975, in evaluate(ex, local_dict, global_dict, out, order, casting, sanitize, _frame_depth, **kwargs)
    974 else:
--> [975](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/numexpr/necompiler.py:975)     raise e

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/numexpr/necompiler.py:874, in validate(ex, local_dict, global_dict, out, order, casting, _frame_depth, sanitize, **kwargs)
    873 names, ex_uses_vml = _names_cache[expr_key]
--> [874](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/numexpr/necompiler.py:874) arguments = getArguments(names, local_dict, global_dict, _frame_depth=_frame_depth)
    876 # Create a signature

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/numexpr/necompiler.py:764, in getArguments(names, local_dict, global_dict, _frame_depth)
    763 except KeyError:
--> [764](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/numexpr/necompiler.py:764)     a = global_dict[name]
    765 arguments.append(numpy.asarray(a))

KeyError: 'util_test'

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/dataset/__init__.py:422, in DataTree.setup_flow(self, *args, **kwargs)
    421 try:
--> [422](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/dataset/__init__.py:422)     return super().setup_flow(*args, **kwargs)
    423 except ValueError as err:

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:1535, in DataTree.setup_flow(self, definition_spec, cache_dir, name, dtype, boundscheck, error_model, nopython, fastmath, parallel, readme, flow_library, extra_hash_data, write_hash_audit, hashing_level, dim_exclude, with_root_node_name)
   1533 from .flows import Flow
-> [1535](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:1535) return Flow(
   1536     self,
   1537     definition_spec,
   1538     cache_dir=cache_dir or self.cache_dir,
   1539     name=name,
   1540     dtype=dtype,
   1541     boundscheck=boundscheck,
   1542     nopython=nopython,
   1543     fastmath=fastmath,
   1544     parallel=parallel,
   1545     readme=readme,
   1546     flow_library=flow_library,
   1547     extra_hash_data=extra_hash_data,
   1548     hashing_level=hashing_level,
   1549     error_model=error_model,
   1550     write_hash_audit=write_hash_audit,
   1551     dim_order=self.dim_order,
   1552     dim_exclude=dim_exclude,
   1553     with_root_node_name=with_root_node_name,
   1554 )

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/flows.py:1053, in Flow.__new__(cls, tree, defs, error_model, cache_dir, name, dtype, boundscheck, nopython, fastmath, parallel, readme, flow_library, extra_hash_data, write_hash_audit, hashing_level, dim_order, dim_exclude, bool_wrapping, with_root_node_name, parallel_irunner, parallel_idotter)
   1052 # otherwise finish normal init
-> [1053](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/flows.py:1053) self.__initialize_2(
   1054     defs,
   1055     error_model=error_model,
   1056     name=name,
   1057     dtype=dtype,
   1058     boundscheck=boundscheck,
   1059     nopython=nopython,
   1060     fastmath=fastmath,
   1061     readme=readme,
   1062     parallel=parallel,
   1063     extra_hash_data=extra_hash_data,
   1064     write_hash_audit=write_hash_audit,
   1065     with_root_node_name=with_root_node_name,
   1066     parallel_idotter=parallel_idotter,
   1067     parallel_irunner=parallel_irunner,
   1068 )
   1069 if flow_library is not None:

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/flows.py:1620, in Flow.__initialize_2(self, defs, error_model, name, dtype, boundscheck, nopython, fastmath, readme, parallel, extra_hash_data, write_hash_audit, with_root_node_name, parallel_irunner, parallel_idotter)
   1619 if self._hashing_level <= 1:
-> [1620](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/flows.py:1620)     func_code, all_name_tokens = self.init_sub_funcs(
   1621         defs,
   1622         error_model=error_model,
   1623         boundscheck=boundscheck,
   1624         nopython=nopython,
   1625         fastmath=fastmath,
   1626     )
   1627     self._func_code = func_code

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/flows.py:1546, in Flow.init_sub_funcs(self, defs, error_model, boundscheck, nopython, fastmath)
   1545     logger.error(f"unable to rewrite '{k}' to itself")
-> [1546](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/flows.py:1546)     raise ValueError(f"unable to rewrite '{k}' to itself")
   1547 logger.debug(f"[{k}] rewrite {init_expr} -> {expr}")

ValueError: unable to rewrite 'util_test' to itself

The above exception was the direct cause of the following exception:

ValueError                                Traceback (most recent call last)
File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:1073, in DataTree.eval(self, expression, engine, dtype, name, with_coords)
   1072 try:
-> [1073](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:1073)     result = self.get_expr(
   1074         expression,
   1075         "numexpr",
   1076         allow_native=False,
   1077         dtype=dtype,
   1078         with_coords=with_coords,
   1079     )
   1080 except Exception:

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:964, in DataTree.get_expr(self, expression, engine, allow_native, dtype, with_coords, parser)
    962         dtype = "float32"
    963     result = (
--> [964](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:964)         self.setup_flow({expression: expression}, dtype=dtype)
    965         .load_dataarray()
    966         .isel(expressions=0)
    967     )
    968 else:

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/dataset/__init__.py:426, in DataTree.setup_flow(self, *args, **kwargs)
    425 if regex:
--> [426](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/dataset/__init__.py:426)     raise ValueError(
    427         f"Setup failed for variable {regex.group(1)}.  Check the expression "
    428         f"and the names of the variables in the dataset."
    429     ) from err
    430 else:

ValueError: Setup failed for variable 'util_test'.  Check the expression and the names of the variables in the dataset.

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:942, in DataTree.get_expr(self, expression, engine, allow_native, dtype, with_coords, parser)
    941     else:
--> [942](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:942)         raise KeyError
    943 except (KeyError, IndexError):

KeyError: 

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/dataset/__init__.py:422, in DataTree.setup_flow(self, *args, **kwargs)
    421 try:
--> [422](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/dataset/__init__.py:422)     return super().setup_flow(*args, **kwargs)
    423 except ValueError as err:

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:1535, in DataTree.setup_flow(self, definition_spec, cache_dir, name, dtype, boundscheck, error_model, nopython, fastmath, parallel, readme, flow_library, extra_hash_data, write_hash_audit, hashing_level, dim_exclude, with_root_node_name)
   1533 from .flows import Flow
-> [1535](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:1535) return Flow(
   1536     self,
   1537     definition_spec,
   1538     cache_dir=cache_dir or self.cache_dir,
   1539     name=name,
   1540     dtype=dtype,
   1541     boundscheck=boundscheck,
   1542     nopython=nopython,
   1543     fastmath=fastmath,
   1544     parallel=parallel,
   1545     readme=readme,
   1546     flow_library=flow_library,
   1547     extra_hash_data=extra_hash_data,
   1548     hashing_level=hashing_level,
   1549     error_model=error_model,
   1550     write_hash_audit=write_hash_audit,
   1551     dim_order=self.dim_order,
   1552     dim_exclude=dim_exclude,
   1553     with_root_node_name=with_root_node_name,
   1554 )

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/flows.py:1053, in Flow.__new__(cls, tree, defs, error_model, cache_dir, name, dtype, boundscheck, nopython, fastmath, parallel, readme, flow_library, extra_hash_data, write_hash_audit, hashing_level, dim_order, dim_exclude, bool_wrapping, with_root_node_name, parallel_irunner, parallel_idotter)
   1052 # otherwise finish normal init
-> [1053](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/flows.py:1053) self.__initialize_2(
   1054     defs,
   1055     error_model=error_model,
   1056     name=name,
   1057     dtype=dtype,
   1058     boundscheck=boundscheck,
   1059     nopython=nopython,
   1060     fastmath=fastmath,
   1061     readme=readme,
   1062     parallel=parallel,
   1063     extra_hash_data=extra_hash_data,
   1064     write_hash_audit=write_hash_audit,
   1065     with_root_node_name=with_root_node_name,
   1066     parallel_idotter=parallel_idotter,
   1067     parallel_irunner=parallel_irunner,
   1068 )
   1069 if flow_library is not None:

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/flows.py:1620, in Flow.__initialize_2(self, defs, error_model, name, dtype, boundscheck, nopython, fastmath, readme, parallel, extra_hash_data, write_hash_audit, with_root_node_name, parallel_irunner, parallel_idotter)
   1619 if self._hashing_level <= 1:
-> [1620](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/flows.py:1620)     func_code, all_name_tokens = self.init_sub_funcs(
   1621         defs,
   1622         error_model=error_model,
   1623         boundscheck=boundscheck,
   1624         nopython=nopython,
   1625         fastmath=fastmath,
   1626     )
   1627     self._func_code = func_code

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/flows.py:1546, in Flow.init_sub_funcs(self, defs, error_model, boundscheck, nopython, fastmath)
   1545     logger.error(f"unable to rewrite '{k}' to itself")
-> [1546](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/flows.py:1546)     raise ValueError(f"unable to rewrite '{k}' to itself")
   1547 logger.debug(f"[{k}] rewrite {init_expr} -> {expr}")

ValueError: unable to rewrite 'util_test' to itself

The above exception was the direct cause of the following exception:

ValueError                                Traceback (most recent call last)
Cell In[20], [line 5](vscode-notebook-cell:?execution_count=20&line=5)
      1 modelname = "tour_mode_choice"
      3 from activitysim.estimation.larch import component_model
----> [5](vscode-notebook-cell:?execution_count=20&line=5) model, data = component_model(
      6     modelname,
      7     edb_directory=f"output/estimation_data_bundle/{modelname}/",
      8     return_data=True,
      9 )

File ~/GitHub/tmp/activitysim/activitysim/estimation/larch/__init__.py:64, in component_model(name, *args, **kwargs)
     62     m = globals().get(f"{name}_model")
     63     if m:
---> [64](https://file+.vscode-resource.vscode-cdn.net/Users/amarin/GitHub/tmp/activitysim/example_estimation/~/GitHub/tmp/activitysim/activitysim/estimation/larch/__init__.py:64)         return m(*args, **kwargs)
     65     raise KeyError(f"no known {name}_model")
     66 else:

File ~/GitHub/tmp/activitysim/activitysim/estimation/larch/mode_choice.py:136, in tour_mode_choice_model(name, edb_directory, return_data)
    131 def tour_mode_choice_model(
    132     name="tour_mode_choice",
    133     edb_directory="output/estimation_data_bundle/{name}/",
    134     return_data=False,
    135 ):
--> [136](https://file+.vscode-resource.vscode-cdn.net/Users/amarin/GitHub/tmp/activitysim/example_estimation/~/GitHub/tmp/activitysim/activitysim/estimation/larch/mode_choice.py:136)     return mode_choice_model(
    137         name=name,
    138         edb_directory=edb_directory,
    139         return_data=return_data,
    140     )

File ~/GitHub/tmp/activitysim/activitysim/estimation/larch/mode_choice.py:110, in mode_choice_model(name, edb_directory, return_data, override_filenames)
    107         model.datatree = d
    108         model.choice_co_code = "override_choice_code"
--> [110](https://file+.vscode-resource.vscode-cdn.net/Users/amarin/GitHub/tmp/activitysim/example_estimation/~/GitHub/tmp/activitysim/activitysim/estimation/larch/mode_choice.py:110) mg = lx.ModelGroup(m.values())
    111 explicit_value_parameters(mg)
    112 apply_coefficients(coefficients, mg)

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/model_group.py:62, in ModelGroup.__init__(self, models, title)
     60 else:
     61     self._submodels.append(model)
---> [62](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/model_group.py:62) self._parameter_bucket.attach_model(model)

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/param_core.py:104, in ParameterBucket.attach_model(self, model, name, agg, unmangle)
    102     model.unmangle(structure_only=True)
    103 elif unmangle:
--> [104](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/param_core.py:104)     model.unmangle()
    105 self._models[name] = model
    107 # collect parameters from the incoming model to be attached, to be able
    108 # to update the bucket's parameters if they are not already present

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/jaxmodel.py:165, in Model.unmangle(self, force, structure_only)
    163 try:
    164     setattr(self, marker, True)
--> [165](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/jaxmodel.py:165)     super().unmangle(force=force, structure_only=structure_only)
    166     for mix in self.mixtures:
    167         mix.prep(self._parameter_bucket)

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/numbamodel.py:1192, in NumbaModel.unmangle(self, force, structure_only)
   1190 if not structure_only:
   1191     if self._dataset is None or force:
-> [1192](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/numbamodel.py:1192)         self.reflow_data_arrays()
   1193     if self._fixed_arrays is None or force:
   1194         self._rebuild_fixed_arrays()

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/jaxmodel.py:177, in Model.reflow_data_arrays(self)
    175 """Reload the internal data_arrays so they are consistent with the datatree."""
    176 if self.compute_engine != "jax":
--> [177](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/jaxmodel.py:177)     return super().reflow_data_arrays()
    179 if self.graph is None:
    180     self._data_arrays = None

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/numbamodel.py:1058, in NumbaModel.reflow_data_arrays(self)
   1055 from .data_arrays import prepare_data
   1057 logger.debug(f"Model.datatree.cache_dir = {datatree.cache_dir}")
-> [1058](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/numbamodel.py:1058) self.dataset, self.dataflows = prepare_data(
   1059     datasource=datatree,
   1060     request=self,
   1061     float_dtype=self.float_dtype,
   1062     cache_dir=datatree.cache_dir,
   1063     flows=self.dataflows,
   1064     make_unused_flows=self.use_streaming,
   1065 )
   1066 if self.use_streaming:
   1067     # when streaming the dataset created above is a vestigial
   1068     # one-case dataset, really we just want the flows, so we
   1069     # get rid of the dataset now
   1070     self._dataset = None

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/data_arrays.py:159, in prepare_data(datasource, request, float_dtype, cache_dir, flows, make_unused_flows)
    157 if "co" in request:
    158     log.debug(f"requested co data: {request['co']}")
--> [159](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/data_arrays.py:159)     model_dataset, flows["co"] = _prep_co(
    160         model_dataset,
    161         datatree_co,
    162         request["co"],
    163         tag="co",
    164         dtype=float_dtype,
    165         cache_dir=cache_dir,
    166         flow=flows.get("co"),
    167     )
    168 if "ca" in request:
    169     log.debug(f"requested ca data: {request['ca']}")

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/data_arrays.py:637, in _prep_co(model_dataset, shared_data_co, vars_co, tag, preserve_vars, dtype, dim_name, cache_dir, flow, use_array_maker, use_eval)
    635     return model_dataset, None
    636 if use_eval:
--> [637](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/model/data_arrays.py:637)     arr = shared_data_co.eval_many(
    638         vars_co, dtype=dtype, result_type="dataarray", with_coords=False
    639     ).values
    640 else:
    641     flowname = flownamer(tag, vars_co, shared_data_co._hash_features())

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:1158, in DataTree.eval_many(self, expressions, engine, dtype, result_type, with_coords)
   1156 arrays = {}
   1157 for k, v in expressions.items():
-> [1158](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:1158)     a = self.eval(
   1159         v, engine=engine, dtype=dtype, name=k, with_coords=with_coords
   1160     )
   1161     if "expressions" in a.coords:
   1162         a = a.drop_vars("expressions")

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:1081, in DataTree.eval(self, expression, engine, dtype, name, with_coords)
   1073         result = self.get_expr(
   1074             expression,
   1075             "numexpr",
   (...)
   1078             with_coords=with_coords,
   1079         )
   1080     except Exception:
-> [1081](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:1081)         result = self.get_expr(
   1082             expression,
   1083             "sharrow",
   1084             allow_native=False,
   1085             dtype=dtype,
   1086             with_coords=with_coords,
   1087         )
   1088 else:
   1089     result = self.get_expr(
   1090         expression,
   1091         engine,
   (...)
   1094         with_coords=with_coords,
   1095     )

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:[948](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/sharrow/relationships.py:948), in DataTree.get_expr(self, expression, engine, allow_native, dtype, with_coords, parser)
    945     if dtype is None:
    946         dtype = "float32"
    947     result = (
--> 948         self.setup_flow({expression: expression}, dtype=dtype)
    949         .load_dataarray()
    950         .isel(expressions=0)
    951     )
    952 elif engine == "numexpr":
    953     import numexpr as ne

File /opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/dataset/__init__.py:[426](https://file+.vscode-resource.vscode-cdn.net/opt/homebrew/Caskroom/miniforge/base/envs/asim-dev/lib/python3.10/site-packages/larch/dataset/__init__.py:426), in DataTree.setup_flow(self, *args, **kwargs)
    424 regex = re.match("^unable to rewrite (.*) to itself$", str(err))
    425 if regex:
--> 426     raise ValueError(
    427         f"Setup failed for variable {regex.group(1)}.  Check the expression "
    428         f"and the names of the variables in the dataset."
    429     ) from err
    430 else:
    431     raise err

ValueError: Setup failed for variable 'util_test'.  Check the expression and the names of the variables in the dataset.

asiripanich avatar Jun 24 '25 13:06 asiripanich

@asiripanich, this is a sharrow-related error. I'll take a look at it and see if I can figure out what's wrong...

Thanks @dhensle for your response.

I ran into an issue today while trying to add a new utility term to the tour mode choice spec of my tour mode choice EDB. This might be a bug or something that needs to be explained a bit more in the documentation. (However, I didn't have any issues modifying or adding new terms to the auto_ownership model. Despite this minor issue, I'm loving this new capability!)

When I tried to add a new line to my tour mode choice spec file, I kept getting this error:

modelname = "tour_mode_choice"

from activitysim.estimation.larch import component_model

model, data = component_model(
    modelname,
    edb_directory=f"output/estimation_data_bundle/{modelname}/",
    return_data=True,
)

> loading from output/estimation_data_bundle/tour_mode_choice/tour_mode_choice_coefficients.csv
> loading from output/estimation_data_bundle/tour_mode_choice/tour_mode_choice_coefficients_template.csv
> loading spec from output/estimation_data_bundle/tour_mode_choice/tour_mode_choice_SPEC.csv
> loading from output/estimation_data_bundle/tour_mode_choice/tour_mode_choice_values_combined.parquet
> unable to rewrite 'util_test' to itself

I tested this using example_estimation and 17_tour_mode_choice.ipynb from this PR.

I added these following lines:

tour_mode_choice_SPEC.csv

util_test,Drive alone not available for escort tours,1,coef_test,,,,,,,,,,,,,,,,,,,,

tour_mode_choice_coefficients_template.csv

coef_test,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_school_univ,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_school_univ,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork

tour_mode_choice_coefficients.csv

coef_test_eatout_escort_othdiscr_othmaint_shopping_social_work_atwork,0,F
coef_test_school_univ,0,F

Any advice on what I might be doing wrong would be greatly appreciated. šŸ˜€

See full error

jpn-- avatar Jun 24 '25 16:06 jpn--

@asiripanich, turns out you were doing nothing wrong, you just happened to find a bug -- a couple arguments were missing from the code to re-estimate the mode choice models. I've fixed it, and taken your edits to create an example / unit test of re-estimation on the tour mode choice notebook. If you try again it should work now. šŸ˜„

jpn-- avatar Jun 26 '25 03:06 jpn--

@asiripanich, turns out you were doing nothing wrong, you just happened to find a bug -- a couple arguments were missing from the code to re-estimate the mode choice models. I've fixed it, and taken your edits to create an example / unit test of re-estimation on the tour mode choice notebook. If you try again it should work now. šŸ˜„

@jpn-- Brilliant! I was away and haven't had a chance to test the new changes in this PR yet, but I'm looking forward to trying it out. I remember seeing this feature was planned for v1.4.0... is there an updated timeline for its release?

asiripanich avatar Jul 22 '25 01:07 asiripanich

Would it be possible to confirm that this issue still exists #595 ? Also #752 please.

bhargavasana avatar Aug 13 '25 19:08 bhargavasana

@dhensle please confirm that this PR fixes #897

bhargavasana avatar Aug 26 '25 02:08 bhargavasana

@dhensle please confirm that this PR fixes #897

Yes, closed that issue under the assumption this will be pulled in shortly.

dhensle avatar Aug 26 '25 16:08 dhensle