Skip to content

Relationship

Class for agent relationships.

Source code in titan/agent.py
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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
class Relationship:
    """Class for agent relationships."""

    # class variable for relationship creation
    next_rel_id = 0

    @classmethod
    def update_id_counter(cls, last_id):
        cls.next_rel_id = last_id + 1

    def __init__(
        self,
        agent1: Agent,
        agent2: Agent,
        duration: int,
        bond_type: str,
        id: Optional[int] = None,
    ):
        """
        Constructor for a Relationship

        args:
            agent1: first agent
            agent2: second agent
            duration: target duration of relationship
            bond_type: type of bond for the relationship [params.classes.bond_types]
            id: unique identifier
        """
        # make sure these agents can be in a relationship
        assert agent1 != agent2, "Cannot create relationship with same agent"
        for rel in agent1.relationships:
            assert agent2 != rel.get_partner(agent1), "Agents already partnered!"

        # self.id is unique ID number used to track each person agent.
        self.agent1 = agent1
        self.agent2 = agent2

        if id is not None:
            self.id = id
        else:
            self.id = self.next_rel_id

        self.update_id_counter(self.id)

        # Relationship properties
        self.duration = duration
        self.total_duration = duration
        self.bond_type = bond_type

        self.bond()

    def __eq__(self, other) -> bool:
        return self.id == other.id

    def __ne__(self, other) -> bool:
        return self.id != other.id

    def __hash__(self) -> int:
        return self.id

    def progress(self, force: bool = False) -> bool:
        """
        Progress a relationship to the next time step (decrementing remaining target duration), or end a relationship if the duration is 0 or if `force` is set to `True`

        args:
            force: whether to force the relationship to end
        """
        if self.duration <= 0 or force:
            self.unbond()
            return True
        else:
            self.duration -= 1
            return False

    def bond(self) -> None:
        """
        Bond two agents. Adds the relationship to each agent's `relationships` set, then adds each partner to the others' partner list.
        """

        # Append relationship to relationships list for each agent
        self.agent1.relationships.add(self)
        self.agent2.relationships.add(self)

        # Pair agent with partner and partner with agent
        self.agent1.partners[self.bond_type].add(self.agent2)
        self.agent2.partners[self.bond_type].add(self.agent1)

    def unbond(self):
        """
        Unbond two agents. Removes relationship from relationship sets.
        Removes partners in each others' partner list.
        """

        # Remove relationship to relationships list for each agent
        self.agent1.relationships.remove(self)
        self.agent2.relationships.remove(self)

        # Unpair agent with partner and partner with agent
        self.agent1.partners[self.bond_type].remove(self.agent2)
        self.agent2.partners[self.bond_type].remove(self.agent1)

    def get_partner(self, agent: "Agent") -> "Agent":
        """
        Given an agent in the relationship, return the other agent

        args:
            agent: one of the agents in the relationship

        returns:
            the agent's partner
        """
        if agent == self.agent1:
            return self.agent2
        elif agent == self.agent2:
            return self.agent1
        else:
            raise ValueError("Agent must be in this relationship")

    def get_number_of_sex_acts(self, rand_gen) -> int:
        """
        Number of sex acts in the relationship during the time step.

        args:
            rand_gen: np random number generator (e.g. self.run_random in model)

        returns:
            number of sex acts
        """
        agent = safe_random_choice([self.agent1, self.agent2], rand_gen)
        freq_params = agent.location.params.partnership.sex.frequency[self.bond_type]

        if freq_params.type == "bins":
            i = get_independent_bin(rand_gen, freq_params.bins)
            return safe_random_int(
                freq_params.bins[i].min, freq_params.bins[i].max, rand_gen
            )

        elif freq_params.type == "distribution":
            return round(safe_dist(freq_params.distribution, rand_gen))

        else:
            raise Exception("Sex acts must be defined as bin or distribution")

    def __str__(self):
        return (
            f"\t{self.id}\t{self.agent1.id}\t{self.agent2.id}\t{self.duration}\t"
            f"{self.bond_type} "
        )

    def __repr__(self):
        return str(self.id)

__init__(agent1, agent2, duration, bond_type, id=None)

Constructor for a Relationship

Parameters:

Name Type Description Default
agent1 Agent

first agent

required
agent2 Agent

second agent

required
duration int

target duration of relationship

required
bond_type str

type of bond for the relationship [params.classes.bond_types]

required
id Optional[int]

unique identifier

None
Source code in titan/agent.py
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
def __init__(
    self,
    agent1: Agent,
    agent2: Agent,
    duration: int,
    bond_type: str,
    id: Optional[int] = None,
):
    """
    Constructor for a Relationship

    args:
        agent1: first agent
        agent2: second agent
        duration: target duration of relationship
        bond_type: type of bond for the relationship [params.classes.bond_types]
        id: unique identifier
    """
    # make sure these agents can be in a relationship
    assert agent1 != agent2, "Cannot create relationship with same agent"
    for rel in agent1.relationships:
        assert agent2 != rel.get_partner(agent1), "Agents already partnered!"

    # self.id is unique ID number used to track each person agent.
    self.agent1 = agent1
    self.agent2 = agent2

    if id is not None:
        self.id = id
    else:
        self.id = self.next_rel_id

    self.update_id_counter(self.id)

    # Relationship properties
    self.duration = duration
    self.total_duration = duration
    self.bond_type = bond_type

    self.bond()

bond()

Bond two agents. Adds the relationship to each agent's relationships set, then adds each partner to the others' partner list.

Source code in titan/agent.py
263
264
265
266
267
268
269
270
271
272
273
274
def bond(self) -> None:
    """
    Bond two agents. Adds the relationship to each agent's `relationships` set, then adds each partner to the others' partner list.
    """

    # Append relationship to relationships list for each agent
    self.agent1.relationships.add(self)
    self.agent2.relationships.add(self)

    # Pair agent with partner and partner with agent
    self.agent1.partners[self.bond_type].add(self.agent2)
    self.agent2.partners[self.bond_type].add(self.agent1)

get_number_of_sex_acts(rand_gen)

Number of sex acts in the relationship during the time step.

Parameters:

Name Type Description Default
rand_gen

np random number generator (e.g. self.run_random in model)

required

Returns:

Type Description
int

number of sex acts

Source code in titan/agent.py
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
def get_number_of_sex_acts(self, rand_gen) -> int:
    """
    Number of sex acts in the relationship during the time step.

    args:
        rand_gen: np random number generator (e.g. self.run_random in model)

    returns:
        number of sex acts
    """
    agent = safe_random_choice([self.agent1, self.agent2], rand_gen)
    freq_params = agent.location.params.partnership.sex.frequency[self.bond_type]

    if freq_params.type == "bins":
        i = get_independent_bin(rand_gen, freq_params.bins)
        return safe_random_int(
            freq_params.bins[i].min, freq_params.bins[i].max, rand_gen
        )

    elif freq_params.type == "distribution":
        return round(safe_dist(freq_params.distribution, rand_gen))

    else:
        raise Exception("Sex acts must be defined as bin or distribution")

get_partner(agent)

Given an agent in the relationship, return the other agent

Parameters:

Name Type Description Default
agent Agent

one of the agents in the relationship

required

Returns:

Type Description
Agent

the agent's partner

Source code in titan/agent.py
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
def get_partner(self, agent: "Agent") -> "Agent":
    """
    Given an agent in the relationship, return the other agent

    args:
        agent: one of the agents in the relationship

    returns:
        the agent's partner
    """
    if agent == self.agent1:
        return self.agent2
    elif agent == self.agent2:
        return self.agent1
    else:
        raise ValueError("Agent must be in this relationship")

progress(force=False)

Progress a relationship to the next time step (decrementing remaining target duration), or end a relationship if the duration is 0 or if force is set to True

Parameters:

Name Type Description Default
force bool

whether to force the relationship to end

False
Source code in titan/agent.py
249
250
251
252
253
254
255
256
257
258
259
260
261
def progress(self, force: bool = False) -> bool:
    """
    Progress a relationship to the next time step (decrementing remaining target duration), or end a relationship if the duration is 0 or if `force` is set to `True`

    args:
        force: whether to force the relationship to end
    """
    if self.duration <= 0 or force:
        self.unbond()
        return True
    else:
        self.duration -= 1
        return False

unbond()

Unbond two agents. Removes relationship from relationship sets. Removes partners in each others' partner list.

Source code in titan/agent.py
276
277
278
279
280
281
282
283
284
285
286
287
288
def unbond(self):
    """
    Unbond two agents. Removes relationship from relationship sets.
    Removes partners in each others' partner list.
    """

    # Remove relationship to relationships list for each agent
    self.agent1.relationships.remove(self)
    self.agent2.relationships.remove(self)

    # Unpair agent with partner and partner with agent
    self.agent1.partners[self.bond_type].remove(self.agent2)
    self.agent2.partners[self.bond_type].remove(self.agent1)