-
Notifications
You must be signed in to change notification settings - Fork 133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Injectable Memoization #138
Conversation
If you have a utility function registered as an injectable but it is not being automatically called you can now get caching of results based on the inputs so long as the inputs are all hashable. Use the memoize=True keyword along with autocall=False to get this behavior. Note that this only applies if you're using the utility via the simulation framework (via injection or get_injectable).
Want to merge to master and I'll try it out? Or should I try out and then merge? |
I'll merge it, I think it's okay. One thing I noticed is that you're generally not using these utility functions via the simulation framework, you're calling them directly since they are in the global scope. You won't get the memoization going that route, you need to have the function injected by the simulation framework so that you get the version that's been wrapped up with caching. |
That makes sense - I'll take a look. |
Hmm - it looks to me like they're already injectables. Is there a place where they're used directly as functions that I need to change? https://github.com/synthicity/bayarea_urbansim/blob/master/variables.py#L125 |
See line 147 where you're calling To get the wrapped function from the simulation framework change line 146 to: @sim.injectable('parcel_sales_price_sqft_func', autocall=False)
def parcel_sales_price_sqft(use, parcel_average_price):
s = parcel_average_price(use)
... Then the wrapped version of the |
OK, that's right. The problem is that |
I see. Hmm. One option is to use the things described in the "Running Sim Components a la Carte" section of the docs: for use in uses:
sales_price = sim.eval_variable('parcel_sales_price_sqft_func', use=use) or for use in uses:
with sim.injectables(use=use):
sales_price = sim.eval_variable('parcel_sales_price_sqft_func') But another thought is whether these functions need to be registered as injectables at all. I might be able to rewire the |
Hmm, I don't love the idea of calling the function from the global scope. I like that the sim framework knows about all the code that gets run for the simulation in a fairly direct way. I think one of the
If the variable |
Right, you're temporarily supplementing any already registered stuff. |
OK I'll try that! |
I've been mulling the idea of iterable injectables. Like you'd register a list and it would serve the contents up one at a time. You could imagine that being used with the 'year' during simulation runs. And if you had two iterable injectables you'd end up with the product of those. But I haven't totally puzzled out all the implications and logistics of that idea. |
The thought had occurred to me, especially since I mainly use this for creating columns that are parameterized (e.g. a price for each land use) and could be returned as a dataframe. |
This enables something like this:
And if you call
my_utility_func
again with the input of1, 2, 3
you'll get back the cached result instead of re-doing the calculation.Memoization requires
autocall=False
and that function inputs always be hashable. The memoization cache follows the same rules as other caching, so you can usecache_scope=
,sim.clear_cache
,sim.disable_cache
, andsim.enable_cache
as usual.