Skip to content

HAART

Bases: BaseFeature

Highly Active Antiretroviral Theray (HAART) is a treatment regimen.

Source code in titan/features/haart.py
 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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
class HAART(base_feature.BaseFeature):
    """
    Highly Active Antiretroviral Theray (HAART) is a treatment regimen.
    """

    name = "haart"
    stats = ["haart"]
    """
        HAART collects the following stats:

        * haart - number of agents with active haart
    """

    counts: ClassVar[Dict] = {}

    def __init__(self, agent: "agent.Agent"):
        super().__init__(agent)

        self.active = False
        self.ever = False
        self.adherent = False

    @classmethod
    def init_class(cls, params: "ObjMap"):
        """
        Initialize the counts dictionary for the races and sex_types in the model.

        args:
            params: the population params
        """
        cls.counts = {
            race: {sex_type: 0 for sex_type in params.classes.sex_types}
            for race in params.classes.races
        }

    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.

        An Agent can only be initialized with HAART if they are HIV+ and diagnosed.  They are randomly assigned to HAART with a probability based on demographics and then if assigned to haart, assigned an adherence based on those same demographic params.

        args:
            pop: the population this agent is a part of
            time: the current time step
        """
        haart_params = (
            self.agent.location.params.demographics[self.agent.race]
            .sex_type[self.agent.sex_type]
            .drug_type[self.agent.drug_type]
            .haart
        )
        if (
            self.agent.hiv.dx  # type: ignore[attr-defined]
            and pop.pop_random.random() < haart_params.init
        ):
            self.initiate(pop.pop_random, haart_params, "init")

    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.

        Account for HIV treatment through highly active antiretroviral therapy (HAART). HAART was implemented in 1996, hence, there is treatment only after 1996. HIV treatment assumes that the agent knows their HIV+ status (`dx` is True).

        args:
            model: the instance of TITAN currently being run
        """
        if (
            self.agent.hiv.dx  # type: ignore[attr-defined]
            and model.time >= model.params.hiv.start_time  # haart starts with hiv
        ):
            # Determine probability of HIV treatment
            haart_params = (
                self.agent.location.params.demographics[self.agent.race]
                .sex_type[self.agent.sex_type]
                .drug_type[self.agent.drug_type]
                .haart
            )
            # Go on HAART
            if not self.active:
                self.enroll(model, haart_params)

            # Update agents on HAART
            else:
                # Go off HAART
                if model.run_random.random() < haart_params.discontinue:
                    self.active = False
                    self.adherent = False
                    self.remove_agent(self.agent)
                # Become non-adherent
                elif (
                    self.adherent
                    and model.run_random.random() < haart_params.adherence.discontinue
                ):
                    self.adherent = False
                # Become adherent
                elif (
                    not self.adherent
                    and model.run_random.random() < haart_params.adherence.become
                ):
                    self.adherent = True

    @classmethod
    def add_agent(cls, agent: "agent.Agent"):
        """
        Add an agent to the class (not instance).

        Increments `counts` or haart agents by race and sex_type for the given agent.

        args:
            agent: the agent to add to the class attributes
        """
        cls.counts[agent.race][agent.sex_type] += 1

    @classmethod
    def remove_agent(cls, agent: "agent.Agent"):
        """
        Remove an agent from the class (not instance).

        Decrements `counts` or haart agents by race and sex_type for the given agent.

        args:
            agent: the agent to remove from the class attributes
        """
        cls.counts[agent.race][agent.sex_type] -= 1

    def set_stats(self, stats: Dict[str, int], time: int):
        if self.active:
            stats["haart"] += 1

    def get_transmission_risk_multiplier(self, time: int, interaction_type: str):
        """
        Get a multiplier for how haart reduces hiv transmission risk based on interaction type and params.

        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])
        """
        prob = 1.0
        if self.active:
            params = self.agent.location.params
            adherence = "adherent" if self.adherent else "non_adherent"
            if interaction_type == "injection":
                prob = params.partnership.injection.transmission.haart_scaling[
                    adherence
                ]
            elif interaction_type == "sex":
                prob = params.partnership.sex.haart_scaling[self.agent.sex_type][
                    adherence
                ]

            # Tuning parameter for ART efficiency
            prob *= params.calibration.haart.transmission

        return prob

    def aids_scale(self):
        prob = 1.0
        if self.active:
            adherence = "adherent" if self.adherent else "non_adherent"
            prob = self.agent.location.params.haart.aids_scale[adherence]

        return prob

    # =========== HELPER METHODS ============

    def enroll(self, model: "model.TITAN", haart_params: ObjMap):
        """
        Determine whether to enroll an agent in HAART.

        args:
            model: the instance of TITAN currently being run
            haart_params: the HAART demographic params for this agent
        """
        if self.agent.location.params.haart.use_cap:
            self.enroll_cap(model, haart_params)
        else:
            self.enroll_prob(model, haart_params)

    def enroll_cap(self, model: "model.TITAN", haart_params: ObjMap):
        """
        Determine whether to enroll an agent in HAART using the cap method.

        args:
            model: the instance of TITAN currently being run
            haart_params: the HAART demographic params for this agent
        """
        race = self.agent.race
        sex_type = self.agent.sex_type
        # if HAART is based on cap instead of prob, determine number of
        # HAART agents based on % of diagnosed agents
        num_dx_agents = self.agent.hiv.dx_counts[race][sex_type]  # type: ignore[attr-defined]
        num_haart_agents = self.counts[race][sex_type]

        # take value from dictionary for cap
        if num_haart_agents < (haart_params.cap * num_dx_agents):
            self.initiate(model.run_random, haart_params, "prob")

    def enroll_prob(self, model: "model.TITAN", haart_params: ObjMap):
        """
        Determine whether to enroll an agent in HAART using probability method.

        args:
            model: the instance of TITAN currently being run
            haart_params: the HAART demographic params for this agent
        """
        if self.ever and self.agent.location.params.haart.use_reinit:
            if model.run_random.random() < haart_params.reinit.prob:
                self.initiate(model.run_random, haart_params, "prob")
        else:
            # Find enroll probability based on time since diagnosis
            enroll_prob = 0.0
            dx_duration = model.time - self.agent.hiv.dx_time  # type: ignore[attr-defined]
            for i in haart_params.enroll.values():
                if i.start <= dx_duration < i.stop:
                    enroll_prob = i.prob * model.calibration.haart.coverage
                    break

            if model.run_random.random() < (enroll_prob):
                self.initiate(model.run_random, haart_params, "prob")

    def initiate(self, rand_gen, haart_params: ObjMap, init_or_prob: str):
        """
        Initiate an agent with HAART and add them to the population.

        args:
            model: the instance of TITAN currently being run
        """
        self.adherent = rand_gen.random() < haart_params.adherence[init_or_prob]

        # Add agent to HAART class set, update agent params
        self.active = True
        self.ever = True
        self.add_agent(self.agent)

stats = ['haart'] class-attribute instance-attribute

HAART collects the following stats:

  • haart - number of agents with active haart

add_agent(agent) classmethod

Add an agent to the class (not instance).

Increments counts or haart agents by race and sex_type for the given agent.

Parameters:

Name Type Description Default
agent Agent

the agent to add to the class attributes

required
Source code in titan/features/haart.py
111
112
113
114
115
116
117
118
119
120
121
@classmethod
def add_agent(cls, agent: "agent.Agent"):
    """
    Add an agent to the class (not instance).

    Increments `counts` or haart agents by race and sex_type for the given agent.

    args:
        agent: the agent to add to the class attributes
    """
    cls.counts[agent.race][agent.sex_type] += 1

enroll(model, haart_params)

Determine whether to enroll an agent in HAART.

Parameters:

Name Type Description Default
model TITAN

the instance of TITAN currently being run

required
haart_params ObjMap

the HAART demographic params for this agent

required
Source code in titan/features/haart.py
177
178
179
180
181
182
183
184
185
186
187
188
def enroll(self, model: "model.TITAN", haart_params: ObjMap):
    """
    Determine whether to enroll an agent in HAART.

    args:
        model: the instance of TITAN currently being run
        haart_params: the HAART demographic params for this agent
    """
    if self.agent.location.params.haart.use_cap:
        self.enroll_cap(model, haart_params)
    else:
        self.enroll_prob(model, haart_params)

enroll_cap(model, haart_params)

Determine whether to enroll an agent in HAART using the cap method.

Parameters:

Name Type Description Default
model TITAN

the instance of TITAN currently being run

required
haart_params ObjMap

the HAART demographic params for this agent

required
Source code in titan/features/haart.py
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
def enroll_cap(self, model: "model.TITAN", haart_params: ObjMap):
    """
    Determine whether to enroll an agent in HAART using the cap method.

    args:
        model: the instance of TITAN currently being run
        haart_params: the HAART demographic params for this agent
    """
    race = self.agent.race
    sex_type = self.agent.sex_type
    # if HAART is based on cap instead of prob, determine number of
    # HAART agents based on % of diagnosed agents
    num_dx_agents = self.agent.hiv.dx_counts[race][sex_type]  # type: ignore[attr-defined]
    num_haart_agents = self.counts[race][sex_type]

    # take value from dictionary for cap
    if num_haart_agents < (haart_params.cap * num_dx_agents):
        self.initiate(model.run_random, haart_params, "prob")

enroll_prob(model, haart_params)

Determine whether to enroll an agent in HAART using probability method.

Parameters:

Name Type Description Default
model TITAN

the instance of TITAN currently being run

required
haart_params ObjMap

the HAART demographic params for this agent

required
Source code in titan/features/haart.py
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
def enroll_prob(self, model: "model.TITAN", haart_params: ObjMap):
    """
    Determine whether to enroll an agent in HAART using probability method.

    args:
        model: the instance of TITAN currently being run
        haart_params: the HAART demographic params for this agent
    """
    if self.ever and self.agent.location.params.haart.use_reinit:
        if model.run_random.random() < haart_params.reinit.prob:
            self.initiate(model.run_random, haart_params, "prob")
    else:
        # Find enroll probability based on time since diagnosis
        enroll_prob = 0.0
        dx_duration = model.time - self.agent.hiv.dx_time  # type: ignore[attr-defined]
        for i in haart_params.enroll.values():
            if i.start <= dx_duration < i.stop:
                enroll_prob = i.prob * model.calibration.haart.coverage
                break

        if model.run_random.random() < (enroll_prob):
            self.initiate(model.run_random, haart_params, "prob")

get_transmission_risk_multiplier(time, interaction_type)

Get a multiplier for how haart reduces hiv transmission risk based on interaction type and params.

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/haart.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
def get_transmission_risk_multiplier(self, time: int, interaction_type: str):
    """
    Get a multiplier for how haart reduces hiv transmission risk based on interaction type and params.

    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])
    """
    prob = 1.0
    if self.active:
        params = self.agent.location.params
        adherence = "adherent" if self.adherent else "non_adherent"
        if interaction_type == "injection":
            prob = params.partnership.injection.transmission.haart_scaling[
                adherence
            ]
        elif interaction_type == "sex":
            prob = params.partnership.sex.haart_scaling[self.agent.sex_type][
                adherence
            ]

        # Tuning parameter for ART efficiency
        prob *= params.calibration.haart.transmission

    return prob

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.

An Agent can only be initialized with HAART if they are HIV+ and diagnosed. They are randomly assigned to HAART with a probability based on demographics and then if assigned to haart, assigned an adherence based on those same demographic 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/haart.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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.

    An Agent can only be initialized with HAART if they are HIV+ and diagnosed.  They are randomly assigned to HAART with a probability based on demographics and then if assigned to haart, assigned an adherence based on those same demographic params.

    args:
        pop: the population this agent is a part of
        time: the current time step
    """
    haart_params = (
        self.agent.location.params.demographics[self.agent.race]
        .sex_type[self.agent.sex_type]
        .drug_type[self.agent.drug_type]
        .haart
    )
    if (
        self.agent.hiv.dx  # type: ignore[attr-defined]
        and pop.pop_random.random() < haart_params.init
    ):
        self.initiate(pop.pop_random, haart_params, "init")

init_class(params) classmethod

Initialize the counts dictionary for the races and sex_types in the model.

Parameters:

Name Type Description Default
params ObjMap

the population params

required
Source code in titan/features/haart.py
32
33
34
35
36
37
38
39
40
41
42
43
@classmethod
def init_class(cls, params: "ObjMap"):
    """
    Initialize the counts dictionary for the races and sex_types in the model.

    args:
        params: the population params
    """
    cls.counts = {
        race: {sex_type: 0 for sex_type in params.classes.sex_types}
        for race in params.classes.races
    }

initiate(rand_gen, haart_params, init_or_prob)

Initiate an agent with HAART and add them to the population.

Parameters:

Name Type Description Default
model

the instance of TITAN currently being run

required
Source code in titan/features/haart.py
232
233
234
235
236
237
238
239
240
241
242
243
244
def initiate(self, rand_gen, haart_params: ObjMap, init_or_prob: str):
    """
    Initiate an agent with HAART and add them to the population.

    args:
        model: the instance of TITAN currently being run
    """
    self.adherent = rand_gen.random() < haart_params.adherence[init_or_prob]

    # Add agent to HAART class set, update agent params
    self.active = True
    self.ever = True
    self.add_agent(self.agent)

remove_agent(agent) classmethod

Remove an agent from the class (not instance).

Decrements counts or haart agents by race and sex_type for the given agent.

Parameters:

Name Type Description Default
agent Agent

the agent to remove from the class attributes

required
Source code in titan/features/haart.py
123
124
125
126
127
128
129
130
131
132
133
@classmethod
def remove_agent(cls, agent: "agent.Agent"):
    """
    Remove an agent from the class (not instance).

    Decrements `counts` or haart agents by race and sex_type for the given agent.

    args:
        agent: the agent to remove from the class attributes
    """
    cls.counts[agent.race][agent.sex_type] -= 1

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.

Account for HIV treatment through highly active antiretroviral therapy (HAART). HAART was implemented in 1996, hence, there is treatment only after 1996. HIV treatment assumes that the agent knows their HIV+ status (dx is True).

Parameters:

Name Type Description Default
model TITAN

the instance of TITAN currently being run

required
Source code in titan/features/haart.py
 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
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.

    Account for HIV treatment through highly active antiretroviral therapy (HAART). HAART was implemented in 1996, hence, there is treatment only after 1996. HIV treatment assumes that the agent knows their HIV+ status (`dx` is True).

    args:
        model: the instance of TITAN currently being run
    """
    if (
        self.agent.hiv.dx  # type: ignore[attr-defined]
        and model.time >= model.params.hiv.start_time  # haart starts with hiv
    ):
        # Determine probability of HIV treatment
        haart_params = (
            self.agent.location.params.demographics[self.agent.race]
            .sex_type[self.agent.sex_type]
            .drug_type[self.agent.drug_type]
            .haart
        )
        # Go on HAART
        if not self.active:
            self.enroll(model, haart_params)

        # Update agents on HAART
        else:
            # Go off HAART
            if model.run_random.random() < haart_params.discontinue:
                self.active = False
                self.adherent = False
                self.remove_agent(self.agent)
            # Become non-adherent
            elif (
                self.adherent
                and model.run_random.random() < haart_params.adherence.discontinue
            ):
                self.adherent = False
            # Become adherent
            elif (
                not self.adherent
                and model.run_random.random() < haart_params.adherence.become
            ):
                self.adherent = True