HopkinsIDD / flepiMoP

The Flexible Epidemic Modeling Pipeline
https://flepimop.org
GNU General Public License v3.0
9 stars 4 forks source link

Transitions get overwritten if there are multiple of the same source->destination transitions #297

Open saraloo opened 1 month ago

saraloo commented 1 month ago

Describe the bug

If multiple transitions are defined for the same source -> destination, gempyor overwrites the rates and proportional to parts of the transition to just the first option.

To Reproduce

The following transition chunks don't work as I expect them to (and do not produce any error). It should create either two transitions with different compartments they are proportional to, but instead they are parsed by gempyor as JUST the first transition.

    - source: ["S", "unvaccinated"]
      destination: ["I", "vaccinated"]
      rate: ["1",["r_vacc","1/T_s"]]
      proportional_to: ["source",[["I"],["unvaccinated","vaccinated"]]]
      proportion_exponent: [
        [["1"],"1"],
        [["1"],"1"]]  

Gempyor reads this as:

Screen Shot 2024-08-14 at 3 41 29 PM

Similarly:

    - source: ["S"]
      destination: ["I"]
      rate: ["beta"]
      proportional_to: ["S", "I"]
      proportion_exponent: ["1", "1"]
    - source: ["S"]
      destination: ["I"]
      rate: ["beta"]
      proportional_to: ["S", "Iv"]
      proportion_exponent: ["1", "1"]

is parsed by gempyor as

Screen Shot 2024-08-14 at 3 43 45 PM

Expected behavior

If we want to restrict this behaviour, we either need to document and provide an error if a user does this in the config, or allow this to happen - just allow the transition to happen multiple times, or sum the rates (not sure how this interacts with the rest of the code)

TimothyWillard commented 4 weeks ago

I'm taking a look at this using the following sample config:

name: minimal
setup_name: minimal
start_date: 2020-02-01
end_date: 2020-02-15
nslots: 15

subpop_setup:
  geodata: data/geodata.csv
  mobility: data/mobility.txt

compartments:
  infection_stage: ["S", "I"]
  vaccination_stage: ["vaccinated", "unvaccinated"]

seir:
  parameters:
    r_vacc:
      value:
        distribution: fixed
        value: 0.5
    T_s:
      value:
        distribution: fixed
        value: 4.0
  transitions:
        - source: ["S", "unvaccinated"]
          destination: ["I", "vaccinated"]
          rate: ["1", ["r_vacc", "1/T_s"]]
          proportional_to: ["source", [["I"], ["unvaccinated", "vaccinated"]]]
          proportion_exponent: [
            [["1"], "1"],
            [["1"], "1"]] 

And I can reproduce this issue:

(venv) twillard@epid-iss-MBP ~/D/G/H/flepiMoP (main)> python
Python 3.11.9 (main, Apr  2 2024, 08:25:04) [Clang 15.0.0 (clang-1500.3.9.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from gempyor.utils import config
>>> from gempyor import compartments
>>> config.clear()
>>> config.read(user=False)
>>> config.set_file("/Users/twillard/Desktop/GitHub/HopkinsIDD/flepiMoP/flepimop/gempyor_pkg/tests/seir/data/config_compartmental_model_test.yml")
>>> cpts = compartments.Compartments(seir_config=config["seir"], compartments_config=config["compartments"])
>>> cpts.parse_transitions(config["seir"])
              source      destination         rate                                 proportional_to proportion_exponent
0  [S, unvaccinated]  [I, vaccinated]  [1, r_vacc]  [[[S], [unvaccinated]], [[I], [unvaccinated]]]    [[1, 1], [1, 1]]

I see the $\frac{1}{T_s}$ rate is now missing which is problematic, but what exactly are we expecting to happen here? Is the underlying issue here that the rates shape does not match the source/destination? The source has one compartment called "S_unvaccinated", destination has one compartment called "I_vaccinated", and the second entry in rate contains two numbers. Is that the issue? I'm confused what the source and destinations are for the rate parameters are supposed to be in this case.

Also, changing this line:

          rate: ["1", ["r_vacc", "1/T_s"]]

to

          rate: [["1"], ["r_vacc", "1/T_s"]]

has no effect.

twallema commented 4 weeks ago

@saraloo Using the (working) SI2R from #299, I verified that Flepi is indeed summing repeated transitions. To model,

dS/dt = S * beta * (I1 + I2)

In the example below the transition S --> I1 is repeated to model,

name: SI2R
setup_name: USA
start_date: 2023-09-01
end_date: 2024-01-01
nslots: 1

subpop_setup:
  geodata: model_input/data/geodata_2019_statelevel.csv
  mobility: model_input/data/mobility_2011-2015_statelevel.csv

initial_conditions:
  method: plugin
  plugin_file_path: model_input/initial_conditions_SI2R.py

compartments:
  infection_stage: ["S", "I1", "I2", "R"]

seir:
  integration:
    method: rk4
    dt: 1.0
  parameters:
    beta:
      value: 0.75
    T_I:
      value: 5

  transitions:
    # repeat transition
    # ==================
    - source: ["S"]
      destination: ["I1"]
      rate: ["beta"]
      proportional_to: ["source", ["I1"]]
      proportion_exponent: ["1", "1"]
    - source: ["S"]
      destination: ["I1"]
      rate: ["beta"]
      proportional_to: ["source", ["I2"]]
      proportion_exponent: ["1", "1"]
    # ===================
    - source: ["I1"]
      destination: ["I2"]
      rate: ["2/T_I"]
      proportional_to: ["source"]
      proportion_exponent: ["1"]
    - source: ["I2"]
      destination: ["R"]
      rate: ["2/T_I"]
      proportional_to: ["source"]
      proportion_exponent: ["1"]

Is equivalent to,

name: SI2R
setup_name: USA
start_date: 2023-09-01
end_date: 2024-01-01
nslots: 1

subpop_setup:
  geodata: model_input/data/geodata_2019_statelevel.csv
  mobility: model_input/data/mobility_2011-2015_statelevel.csv

initial_conditions:
  method: plugin
  plugin_file_path: model_input/initial_conditions_SI2R.py

compartments:
  infection_stage: ["S", "I1", "I2", "R"]

seir:
  integration:
    method: rk4
    dt: 1.0
  parameters:
    beta:
      value: 0.75
    T_I:
      value: 5

  transitions:
    # using only one transition
    # ==========================
    - source: ["S"]
      destination: ["I1"]
      rate: ["beta"]
      proportional_to: ["source", [[["I1"", I2"]]]]
      proportion_exponent: ["1", "1"]  
    - source: ["I1"]
      destination: ["I2"]
      rate: ["2/T_I"]
      proportional_to: ["source"]
      proportion_exponent: ["1"]
    # ==========================
    - source: ["I2"]
      destination: ["R"]
      rate: ["2/T_I"]
      proportional_to: ["source"]
      proportion_exponent: ["1"]

So I think we can close this issue and keep this SI2R example in the back of our head to rewrite docs at some point.

SI2R.zip

shauntruelove commented 3 weeks ago

@twallema @MacdonaldJoshuaCaleb to add comment explaining the resolution of this for future documentation and awareness.