Skip to content

Proportional initializer fails when ratios is a C-length array and N == C #242

@JavadocMD

Description

@JavadocMD

The documented intention for the Proportional initializer is that you can provide either an NxC-shaped array OR a C-shaped array for the ratios argument. In the case of a C-shaped input, this should be broadcast to NxC (where the input is duplicated across the rows, the N-axis). This mostly works as expected except for the special case where N is equal to C (there are the same number of nodes as IPM compartments). The broadcasting happens in the wrong direction (across the C-axis), which eventually raises an error or returns unexpected results depending on the values used.

We should make sure to explicitly interpret 1-dimensional inputs as C-values when broadcasting.

While we're at it, the error messaging around invalid ratios forms could be improved.

Example 1: raises an error

from epymorph.kit import *

init.Proportional(ratios=[0.9, 0.1, 0.0]).with_context(
    scope=CustomScope(["A", "B", "C"]),
    ipm=ipm.SIRS(),
    params={
        "population": [100, 200, 300],
    },
).evaluate()
---------------------------------------------------------------------------
InitError                                 Traceback (most recent call last)
Cell In[3], line 9
      1 from epymorph.kit import *
      3 init.Proportional([0.9, 0.1, 0.0]).with_context(
      4     scope=CustomScope(["A", "B", "C"]),
      5     ipm=ipm.SIRS(),
      6     params={
      7         "population": [100, 200, 300],
      8     },
----> 9 ).evaluate()

File ~/Workspaces/Epymorph/epymorph/simulation.py:528, in SimulationFunctionClass.__new__.<locals>.evaluate(self, *args, **kwargs)
    526 @functools.wraps(orig_evaluate)
    527 def evaluate(self, *args, **kwargs):
--> 528     result = orig_evaluate(self, *args, **kwargs)
    529     self.validate(result)
    530     return result

File ~/Workspaces/Epymorph/epymorph/initializer.py:211, in Proportional.evaluate(self)
    209 if np.any(row_sums <= 0):
    210     err = "One or more rows sum to zero or less."
--> 211     raise InitError(err)
    213 pop = self.data(_POPULATION_ATTR)
    214 result = pop[:, np.newaxis] * (ratios / row_sums[:, np.newaxis])

InitError: One or more rows sum to zero or less.

Example 2: no error but bad results

from epymorph.kit import *

init.Proportional(ratios=[0.5, 0.3, 0.2]).with_context(
    scope=CustomScope(["A", "B", "C"]),
    ipm=ipm.SIRS(),
    params={
        "population": [100, 200, 300],
    },
).evaluate()
array([[ 33,  33,  33],
       [ 67,  67,  67],
       [100, 100, 100]])

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions