Skip to content

Overview

Agent Features

Features from params.features that include an agent attribute (e.g. PrEP, incarceration) are implemented in standalone files that implement the interface from features.BaseFeature. This allows for all of the logic related to a feature to be consolidated in one place and additionally makes incorporating a new feature into the model as simple as possible.

To add a new feature:

  • Add the param to param.features
  • Add a file to features/ which creates a class that is a sub-class of BaseFeature
    • Implement the methods of BaseFeature which are needed for this feature
    • Not all methods are needed for all features (see below for details on the methods)
  • Re-export the feature from features/__init__.py
  • Add tests in tests/features/
  • Add it to the docs in docs/api/features/ and to the nav in mkdocs.yml

The TITAN, Population, and Agent classes all use sub-classes of BaseFeature to initialize the object/call methods as appropriate.

BaseFeature

Interface class for an agent-oriented feature of the TITAN model. It is intended to be inherited by feature classes to ensure that the expected methods/fields exist.

The class takes advantage of both instance and class methods and attributes. The instance methods/attributes are used at the agent level, whereas the class methods/attributes are used at the population level.

A class method can be called on an instance of an object, but it doesn't have access to that instance (e.g. agent.feature.add_agent has to be passed agent because it is a class method).

Source code in titan/features/base_feature.py
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
class BaseFeature:
    """
    Interface class for an agent-oriented feature of the TITAN model.  It is intended to be
    inherited by feature classes to ensure that the expected methods/fields exist.

    The class takes advantage of both instance and class methods and attributes. The instance
    methods/attributes are used at the agent level, whereas the class methods/attributes are
    used at the population level.

    A class method can be called on an instance of an object, but it doesn't have access
    to that instance (e.g. agent.feature.add_agent has to be passed agent because it is a class
    method).
    """

    name: str = ""
    """Name of feature in the params file.  Also used to name the attribute in Agent"""

    stats: List[str] = []
    """List of names of stats that come from this feature (e.g. numFeat)"""

    def __init__(self, agent: "agent.Agent"):
        """
        Constructor for an instance of the feature.  This is called from within `Agent.__init__` and passes the agent to the feature to create a two way binding.  All features must have the attributes of `active` and `agent`.  By default `active` is false and `agent` is the passed agent.

        Called on all features whether or not param is enabled to make sure that references to other features within a feature do not cause errors (e.g. incar referring to prep, but prep isn't on).

        args:
            agent: The agent this feature instance is attached to.
        """
        self.active = False
        self.agent = agent

    @classmethod
    def init_class(cls, params):
        """
        Initialize any class level attributes (such as setting counters to zero). Called on every active feature on population initialization.

        args:
            params: parameters for this population
        """
        pass

    def init_agent(self, pop: "population.Population", time: int):
        """
        Initialize the agent for this feature during population initialization (`Population.create_agent`).  Called on only features that are enabled per the params.

        args:
            pop: the population this agent is a part of
            time: the current time step
        """
        pass

    def update_agent(self, model: "model.TITAN"):
        """
        Update the agent for this feature for a time step.  Called once per time step in `TITAN.update_all_agents`. Agent level updates are done after population level updates.   Called on only features that are enabled per the params.

        args:
            model: the instance of TITAN currently being run
        """
        pass

    @classmethod
    def add_agent(cls, agent: "agent.Agent"):
        """
        Add an agent to the class (not instance).  This can be useful if tracking population level statistics or groups, such as counts or newly active agents.

        This method is not called from anywhere in the model, but creates a cohesive api with `remove_agent`, which is called from `Population.remove_agent`.

        args:
            agent: the agent to add to the class attributes
        """
        pass

    @classmethod
    def remove_agent(cls, agent: "agent.Agent"):
        """
        Remove an agent from the class (not instance).  This can be useful if tracking population level statistics or groups, such as counts.

        This method is called from `Population.remove_agent`, but may also need to be called within the feature if an agent transitions from `active == True` to `active == False`.

        args:
            agent: the agent to remove from the class attributes
        """
        pass

    @classmethod
    def update_pop(cls, model: "model.TITAN"):
        """
        Update the feature for the entire population (class method).  This is useful for initializing class level trackers that need to be reset each time step, or if enabling a feature for agents needs to be evaluated within the context of the full population (limited slots, or similar).

        This is called in `TITAN.update_all_agents` before agent-level updates are made.

        args:
            model: the instance of TITAN currently being run
        """
        pass

    def set_stats(self, stats: Dict[str, int], time: int):
        """
        Update the `stats` dictionary passed for this agent.  Called from `output.get_stats` for each enabled feature in the model.

        The stats to be updated must be declared in the class attribute `stats` to make sure the dictionary has the expected keys/counter value initialized.

        args:
            stats: the dictionary to update with this agent's feature statistics
            time: the time step of the model when the stats are set
        """
        pass

    def get_acquisition_risk_multiplier(self, time: int, interaction_type: str):
        """
        Get a multiplier for how this feature affects acquisition of HIV for the given interaction_type.

        By default, returns 1.0

        args:
            time: the current model time step
            interaction_type: The type of interaction where the agent could acquire HIV (e.g. 'sex', 'injection' - from [params.classes.interaction_types])
        """
        return 1.0

    def get_transmission_risk_multiplier(self, time: int, interaction_type: str):
        """
        Get a multiplier for how this feature affects transmission of HIV for the given interaction_type.

        By default, returns 1.0

        args:
            time: the current model time step
            interaction_type: The type of interaction where the agent could transmit HIV (e.g. 'sex', 'injection' - from [params.classes.interaction_types])
        """
        return 1.0

name: str = '' class-attribute instance-attribute

Name of feature in the params file. Also used to name the attribute in Agent

stats: List[str] = [] class-attribute instance-attribute

List of names of stats that come from this feature (e.g. numFeat)

__init__(agent)

Constructor for an instance of the feature. This is called from within Agent.__init__ and passes the agent to the feature to create a two way binding. All features must have the attributes of active and agent. By default active is false and agent is the passed agent.

Called on all features whether or not param is enabled to make sure that references to other features within a feature do not cause errors (e.g. incar referring to prep, but prep isn't on).

Parameters:

Name Type Description Default
agent Agent

The agent this feature instance is attached to.

required
Source code in titan/features/base_feature.py
28
29
30
31
32
33
34
35
36
37
38
def __init__(self, agent: "agent.Agent"):
    """
    Constructor for an instance of the feature.  This is called from within `Agent.__init__` and passes the agent to the feature to create a two way binding.  All features must have the attributes of `active` and `agent`.  By default `active` is false and `agent` is the passed agent.

    Called on all features whether or not param is enabled to make sure that references to other features within a feature do not cause errors (e.g. incar referring to prep, but prep isn't on).

    args:
        agent: The agent this feature instance is attached to.
    """
    self.active = False
    self.agent = agent

add_agent(agent) classmethod

Add an agent to the class (not instance). This can be useful if tracking population level statistics or groups, such as counts or newly active agents.

This method is not called from anywhere in the model, but creates a cohesive api with remove_agent, which is called from Population.remove_agent.

Parameters:

Name Type Description Default
agent Agent

the agent to add to the class attributes

required
Source code in titan/features/base_feature.py
69
70
71
72
73
74
75
76
77
78
79
@classmethod
def add_agent(cls, agent: "agent.Agent"):
    """
    Add an agent to the class (not instance).  This can be useful if tracking population level statistics or groups, such as counts or newly active agents.

    This method is not called from anywhere in the model, but creates a cohesive api with `remove_agent`, which is called from `Population.remove_agent`.

    args:
        agent: the agent to add to the class attributes
    """
    pass

get_acquisition_risk_multiplier(time, interaction_type)

Get a multiplier for how this feature affects acquisition of HIV for the given interaction_type.

By default, returns 1.0

Parameters:

Name Type Description Default
time int

the current model time step

required
interaction_type str

The type of interaction where the agent could acquire HIV (e.g. 'sex', 'injection' - from [params.classes.interaction_types])

required
Source code in titan/features/base_feature.py
117
118
119
120
121
122
123
124
125
126
127
def get_acquisition_risk_multiplier(self, time: int, interaction_type: str):
    """
    Get a multiplier for how this feature affects acquisition of HIV for the given interaction_type.

    By default, returns 1.0

    args:
        time: the current model time step
        interaction_type: The type of interaction where the agent could acquire HIV (e.g. 'sex', 'injection' - from [params.classes.interaction_types])
    """
    return 1.0

get_transmission_risk_multiplier(time, interaction_type)

Get a multiplier for how this feature affects transmission of HIV for the given interaction_type.

By default, returns 1.0

Parameters:

Name Type Description Default
time int

the current model time step

required
interaction_type str

The type of interaction where the agent could transmit HIV (e.g. 'sex', 'injection' - from [params.classes.interaction_types])

required
Source code in titan/features/base_feature.py
129
130
131
132
133
134
135
136
137
138
139
def get_transmission_risk_multiplier(self, time: int, interaction_type: str):
    """
    Get a multiplier for how this feature affects transmission of HIV for the given interaction_type.

    By default, returns 1.0

    args:
        time: the current model time step
        interaction_type: The type of interaction where the agent could transmit HIV (e.g. 'sex', 'injection' - from [params.classes.interaction_types])
    """
    return 1.0

init_agent(pop, time)

Initialize the agent for this feature during population initialization (Population.create_agent). Called on only features that are enabled per the params.

Parameters:

Name Type Description Default
pop Population

the population this agent is a part of

required
time int

the current time step

required
Source code in titan/features/base_feature.py
50
51
52
53
54
55
56
57
58
def init_agent(self, pop: "population.Population", time: int):
    """
    Initialize the agent for this feature during population initialization (`Population.create_agent`).  Called on only features that are enabled per the params.

    args:
        pop: the population this agent is a part of
        time: the current time step
    """
    pass

init_class(params) classmethod

Initialize any class level attributes (such as setting counters to zero). Called on every active feature on population initialization.

Parameters:

Name Type Description Default
params

parameters for this population

required
Source code in titan/features/base_feature.py
40
41
42
43
44
45
46
47
48
@classmethod
def init_class(cls, params):
    """
    Initialize any class level attributes (such as setting counters to zero). Called on every active feature on population initialization.

    args:
        params: parameters for this population
    """
    pass

remove_agent(agent) classmethod

Remove an agent from the class (not instance). This can be useful if tracking population level statistics or groups, such as counts.

This method is called from Population.remove_agent, but may also need to be called within the feature if an agent transitions from active == True to active == False.

Parameters:

Name Type Description Default
agent Agent

the agent to remove from the class attributes

required
Source code in titan/features/base_feature.py
81
82
83
84
85
86
87
88
89
90
91
@classmethod
def remove_agent(cls, agent: "agent.Agent"):
    """
    Remove an agent from the class (not instance).  This can be useful if tracking population level statistics or groups, such as counts.

    This method is called from `Population.remove_agent`, but may also need to be called within the feature if an agent transitions from `active == True` to `active == False`.

    args:
        agent: the agent to remove from the class attributes
    """
    pass

set_stats(stats, time)

Update the stats dictionary passed for this agent. Called from output.get_stats for each enabled feature in the model.

The stats to be updated must be declared in the class attribute stats to make sure the dictionary has the expected keys/counter value initialized.

Parameters:

Name Type Description Default
stats Dict[str, int]

the dictionary to update with this agent's feature statistics

required
time int

the time step of the model when the stats are set

required
Source code in titan/features/base_feature.py
105
106
107
108
109
110
111
112
113
114
115
def set_stats(self, stats: Dict[str, int], time: int):
    """
    Update the `stats` dictionary passed for this agent.  Called from `output.get_stats` for each enabled feature in the model.

    The stats to be updated must be declared in the class attribute `stats` to make sure the dictionary has the expected keys/counter value initialized.

    args:
        stats: the dictionary to update with this agent's feature statistics
        time: the time step of the model when the stats are set
    """
    pass

update_agent(model)

Update the agent for this feature for a time step. Called once per time step in TITAN.update_all_agents. Agent level updates are done after population level updates. Called on only features that are enabled per the params.

Parameters:

Name Type Description Default
model TITAN

the instance of TITAN currently being run

required
Source code in titan/features/base_feature.py
60
61
62
63
64
65
66
67
def update_agent(self, model: "model.TITAN"):
    """
    Update the agent for this feature for a time step.  Called once per time step in `TITAN.update_all_agents`. Agent level updates are done after population level updates.   Called on only features that are enabled per the params.

    args:
        model: the instance of TITAN currently being run
    """
    pass

update_pop(model) classmethod

Update the feature for the entire population (class method). This is useful for initializing class level trackers that need to be reset each time step, or if enabling a feature for agents needs to be evaluated within the context of the full population (limited slots, or similar).

This is called in TITAN.update_all_agents before agent-level updates are made.

Parameters:

Name Type Description Default
model TITAN

the instance of TITAN currently being run

required
Source code in titan/features/base_feature.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
@classmethod
def update_pop(cls, model: "model.TITAN"):
    """
    Update the feature for the entire population (class method).  This is useful for initializing class level trackers that need to be reset each time step, or if enabling a feature for agents needs to be evaluated within the context of the full population (limited slots, or similar).

    This is called in `TITAN.update_all_agents` before agent-level updates are made.

    args:
        model: the instance of TITAN currently being run
    """
    pass