Skip to content

Comments

New distributions code moved down into emod-api from emodpy (Fix #50, was issue 43 in emodpy previously)#50

Merged
ckirkman-IDM merged 36 commits intoEMOD-Hub:mercuryfrom
ckirkman-IDM:43
Dec 4, 2025
Merged

New distributions code moved down into emod-api from emodpy (Fix #50, was issue 43 in emodpy previously)#50
ckirkman-IDM merged 36 commits intoEMOD-Hub:mercuryfrom
ckirkman-IDM:43

Conversation

@ckirkman-IDM
Copy link
Collaborator

@ckirkman-IDM ckirkman-IDM commented Nov 19, 2025

There were a lot of things using the old distributions. Changes in here include, but my memory may (momentarily) be forgetting more:

  • migrated distribution classes from emodpy into emod-api
  • migrated emodpy Demographics#set_X_distribution() methods into emod-api DemographicsBase
  • removed old distribution classes and related conditional code to allow them & new distributions (e.g. AgeDistributionOld is now gone, code won't even try to check for it anymore)
  • restored demographics loading from_file() to support current migration usage.
  • removing Demographics.from_params() and associated migration call into it.
  • Restored many, many tests. Deleted many, many duplicative, outmoded tests of old distributions in emod-api
  • Extended Node tests to ensure above deletions of tests don't leave any gaps. (nodes cover virtually all of Demog data writing)
  • Removed DemographicsTemplates
  • Implicit functions now exist in their own dedicated space: emod_api/demographics/implicit_functions.py
  • Removed many, many old outmoded code from DemographicsBase supporting "raw" editing of default node, users should use the set_X_distribution() methods now.
  • Reworked DemographicsTemplates, which now look like they are identical in design to Demographics. It'd be great to get some thumbs up/down after trying things out from Kurt to delete DemgographicsTemplates (or leave an extremely minor stub).
  • Removed other dead, crusty code like demog_send_over_pipes to reduce maintenance and eliminate future questions over what it is and keeping their tests working for no useful reason. And I admit, also because I wanted to.
  • Loading demographics via Demographics.from_file() now sets implicit functions on its return for config consistency.

from emod_api import schema_to_class as s2c


class BaseDistribution(ABC):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a specific reason to put the distribution classes into separate .py file? I would prefer to merge them into a single file so user can import them more simply:

from emod_api.utils.distributions import BimodalDistribution

Not:
from emod_api.utils.distributions.bimodal_distribution import BimodalDistribution

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One class per file aids in readability and more easily understanding where things are by glancing at file lists. I find this to be highly important. For us (me? :D) and for users, especially since these distributions will be user-facing.

Most users will end up using a small subset at any one time, so their imports will not look so detailed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this break what we did in emodpy-hiv? This seems opposite from our 2.0 pattern for interventions and targeting config.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will require a small update in emodpy-hiv , yes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will it break user code? We really don't want to break anything in emodpy-hiv.

Copy link
Collaborator Author

@ckirkman-IDM ckirkman-IDM Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since emodpy needs to be updated, too, I propose that the emod-api distribution classes be imported into init.py in emodpy.utils.distributions. This will avoid breakage in code.

In fact, this is currently what I have in emodpy for PR'ing.

@ckirkman-IDM ckirkman-IDM changed the title New distributions code moved down into emod-api from emodpy (Fix #43) New distributions code moved down into emod-api from emodpy (Fix #50, was issue 43 in emodpy previously) Nov 20, 2025
Copy link
Collaborator

@Bridenbecker Bridenbecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Most of my comments are about adding comments and creating tickets for changes to emodpy-malaria and emodpy-hiv.

default_node: (Node) Represents default values for all nodes, unless overridden on a per-node basis.
metadata: (Dict) set the demographics metadata to the supplied dictionary. Default yields default
metadata values.
set_defaults: (bool) Whether to set default node attributes on the default node. Defaults to True.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the documentation for the init() method go above it since it is part of creating an object of the class?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh, I have never put such documentation there. I can put it wherever. It would be good to talk about a team-standard and decide (moving forward). As for here, after taking a look at the below, I'll do whatever.

PEP #257 apparently indicates init() is the "proper" place for this. It is the "most liked" solution in this relevant stackoverflow discussion, but by not means the only solution proposed/discussed.

https://stackoverflow.com/questions/37019744/is-there-a-consensus-on-what-should-be-documented-in-the-class-and-init-docs

Copy link
Collaborator

@Bridenbecker Bridenbecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for making all of those changes. the comments about what is used in what disease are really going to be helpful later.

mortality_distribution_female: MortalityDistribution = None,
innate_immune_distribution_flag: int = None,
innate_immune_distribution1: int = None,
innate_immune_distribution2: int = None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My reason for doing the subclassing is that when we support multiple diseases, it helps to the user and developer to know what is supported in what disease.


# Ensure we have the right config items set now.
self.assertEqual(self.config.parameters.Age_Initialization_Distribution_Type, "DISTRIBUTION_SIMPLE")
self.assertEqual(self.config.parameters.Susceptibility_Initialization_Distribution_Type, "DISTRIBUTION_COMPLEX")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it depends on whether implicit function for Susceptibility_Initialization_Distribution_Type cascades up.

input_filename = os.path.join(manifest.demo_folder, "Namawala_four_node_demographics_for_Thomas.json")
output_filename = os.path.join(self.out_folder, "Namawala_four_node_demographics_for_Thomas_comparison.json")

self.pass_through_test(input_filename, output_filename)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fudge

…ither (age, susceptibility) will now throw an exception to prevent ambiguity when a demographics file is being created.
@ckirkman-IDM
Copy link
Collaborator Author

@Bridenbecker
BTW, I added demographics.to_dict() checking of dual complex/simple distribution setting for age and susceptibility. An exception will be thrown if both exist for either (ambiguous). Thus, if someone reads in a demographics.json and then tries to use it for a new simulation, by calling demographics.to_file()/generate_file()/to_dict() it will fail with an appropriate message.

Updating after merging main into mercury, closing PR 43 after this if tests pass.
@ckirkman-IDM ckirkman-IDM merged commit d544df9 into EMOD-Hub:mercury Dec 4, 2025
2 checks passed
@ckirkman-IDM ckirkman-IDM deleted the 43 branch December 4, 2025 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

5 participants