-
Notifications
You must be signed in to change notification settings - Fork 5
Open Design Questions
As of June 15, 2024, here is a working list of open design questions where we have been experimenting with or at least considering alternative approaches.
- Hard-coding Properties? Christopher has been using an approach in which all properties (e.g., age) are adding dynamically and programmatically, potentially based on step function code. Jonathan has been treating the modeled population file headers as the definition of properties but there is also code in the step functions that references specific properties. Which of these do we want to go with? Or do we want a variation on this? Are there any built-in properties? Like age or node? But even with those, one modeler might want an age property which gets updated every timestep and another might want date-of-birth which gets compared to "now" each timestep. And with node, a single node sim might not have a node property. Do we want the core code to be completely agnostic of properties? (Probably) As for adding properties to the model, at some point once the model is loaded/generated, the columns are defined. Some combination of model definition and step function coding freezes the property set.
KM: Argue strongly for dynamic properties. Light agents implies, to me, that the user can make agents that are 'as simple as they need to be, but not simpler'. On the other side, having the flexibility to serve a variety of spatial disease modeling problems implies that the framework will need to accommodate a large set of possible properties, relevant to specific use cases. With hard-coded properties, it seems we must sacrifice one or the other.
- Input Files: Jonathan has been decoupling scenario creation from simulation by having the first step create the modeled population (and births and deaths data) as CSV or .npy files. Christopher has been initializing the population de novo on each run. At some point it comes down to whether we choose to think of model initialization as a separate pre-processing step or as part of running the model itself. See here.
KM: I argue for runtime generation of the parameters of a simulation as the "framework/reference design" standard. When running sims representing real spatial networks, there will be times where of course the simplest way to bring in information will be to have data files that are read rather than executed (population, lat, long etc. by node for a real setting, ...). But I don't think the core model framework should expect a certain set of inputs, formatted a certain way; to the extent that reading data files is necessary to set up a complicated simulation, it's the model user's job to write the "exoskeleton" code that pulls that input into the model. This also, I think, philosophically follows from the above on dynamic properties - extending from agents to nodes, if I don't need certain information or certain processes, I'd rather remove them than have to set parameters to zero... and I think requiring input files at all can easily start down the path towards hard requirements on what those input files contain.
Big caveat - in running large suites of simulations, functionality to record what differentiates one sim from another is crucial! So we'll need ways to record that info. Though not requiring input files per sim enables us to construct a more minimal record, with one instance of 'parameters' shared across simulations, and only per-sim records of the things that change from sim to sim.
- EULAs: Jonathan has been working with the initial Epidemiologically Useless Light Agents as a complete separate cohort that is only modeled in a way that keeps track of population over time. Christopher has not yet optimized ignoring inactive (recovered or deceased) agents. See here for more details.
KM: I believe the general concept of EULA cohorts can be incredibly useful for performance, as well as pushing users to really think about which agents drive the disease dynamics in their model. However, what a EULA really is can change depending on what we're modeling - the classic one is the "R" in an S(E)IR model. But there are other examples - people with access to clean water being uninteresting in a cholera model; non-sexually active agents being uninteresting in an STI model. So a question for software side about whether that implies something about generality vs. specificity of EULAs - e.g., should they be a "piece of the framework / reference design", or whether these are "performance tips / examples" applicable in specific model instantiations. Interested in thoughts. Note that we have not addressed, and may one day need to address, some intricacies that may arise. What happens if EULAs can become interesting one day? How to track things like mortality, planned events that target EULAs? How to ensure that the interesting agent <-> EULA transition is 1:1, so no agent gets double-counted...
- Mortality: Jonathan has been modeling mortality by pre-fitting curves for the EULA populations by node and using pre-calculated lifespans for the modeled population.
KM: Lots of options here, and maintaining flexibility is important. I think an approach where we have a "mortality_step" in the list of model steps, that the user fills in, is a good general reference design. For models of a certain scale, filling a priority queue seems a good option to have few "steps per death" (1 random draw, one pop off of the queue). Giving agents a "date of death" property seems like another one, 1 random draw though potentially many conditional checks. But I don't want to have to foresee things - if people want to have an agent property like "malnutrition" that raises mortality risk in a way we won't know before running the sim, then they should be able to have a more flexible approach. Or say I want a birth rate = mortality rate constant population, I might want to implement "mortality" as just a reset to a newborn state.
- Priority Queues: Jonathan did an investigation as to whether priority queues made sense for things like vaccination. He concluded that the perf didn't pencil out. Chris still interested in doing his own investigation.
KM: I don't have a strong opinion - this seems to me to be more of a "specific model" question rather than a "core framework" question. That is, they may be the most performant/interpretable option for simulations with a certain population or time scale, or for certain processes within a simulation. They make a lot of intuitive sense. But it may be that a different implementation may be necessary at other scales or for other processes in a model. So I think worth looking at "what does this look like for a northern Nigeria model, does it perform well for that?" and if so, we implement it within the "demographic step" functions for that model, in a way that could be replaced for a different model with different needs.
- Julia: Does it make sense to provide a Julia version of our model? What would that entail? What are the pros and cons?
KM: I think we made a functional decision here, and maybe without ever seriously exploring implementation options. To keep momentum going, I think we should continue developing within a Python framework. However, I'd like to identify a point to come back around to this - I expect we will likely hit a place where we have a v0 of this reference design, and now we're thinking about the v1, seeing where choices we made are constraining or awkward, and planning a bit of coding push / refactor. I think that makes a sensible time to revisit the choice of core technology.
(Mostly resolved, but still ironing out details)
-
NumPy accelerations: Jonathan has been experimenting with compiled C
ctypes
extensions which definitely are faster than NumPy but create challenges for portability and also debugging. Christopher has been working with Numba (details TBD). GPUs would be awesome if we can overcome portability challenges. The Taichi package may help here. Rust is "the new hotness" in Python extensions and needs to be considered for "native" LASER extensions/optimizations. _KM: If we align around a single model reference design, where the running of my model consists of discrete steps chained together, then it seems to me that framework could enable the dev or user to choose which steps to accelerate via whichever option they wish - nothing intrinsically stopping someone from something like amodel.steps = [do_births_numpy, do_mortality_numba, do_transmission_C, do migration_Taichi]
. In practice that seems silly, but am I right to think there's nothing stopping that? If so, I think the push should be that alignment. _ -
Support for R users: A layer of R code + Reticulate may be a solution here. Also by decoupling model creation/pre-processing and post-processing, and by running the model as a service, we can enable R users to work almost entirely in R. We don't imagine ever being able to extend the model itself with R unless we take the approach of ctypes extensions to Python and create compiled C step functions which can be called from R.
-
"USEIRD" memory layout + index swapping: Both Jonathan & Christopher have done extensive development & testing of this approach which seemed to offer a lot of promise. There are definite upsides, but downsides include the fact the index management (including UID tracking - implicit when not swapping) is kind of a pain and routine immunization is a lot faster/easier if everyone is age-ordered.
-
Rust: Does it make sense to provide a Rust version of our model? Parts of the model (extensions?) What would that entail? What are the pros and cons?
-
Process nodes separately: Jonathan has been keeping the whole population together with node or patch id as simply 1 integer property like any other. Christopher was experimenting with processing each node separately. There are interesting pros and cons of each approach. It seems we have settled on processing all nodes together and just keeping node as an attribute or property like any other, not a container to hold or organize agents.
-
Migration: Jonathan has been experimenting with moving infected people away (and back again). Christopher has been using the Kurt Frey approach of "migrating infections". Christopher also has node ID (patch ID) as a property, so agent migration rather than contagion migration would be trivial. It seems we will want to support or allow for both of these approaches depending on what the user needs.