Skip to content

Random Trial

name: str

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

stats: List[str]

Random Trial collects the following stats:

  • random_trial - number of agents with active random_trial
  • random_trial_treated - number of active agents treated
  • random_trial_treated_hiv - number of HIV+ agents treated
  • random_trial_suitable - number of active agents suitable

set_stats(self, 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/random_trial.py
def set_stats(self, stats: Dict[str, int], time: int):
    if self.active:
        stats["random_trial"] += 1
        if self.treated:
            stats["random_trial_treated"] += 1
            if self.agent.hiv.active:  # type: ignore[attr-defined]
                stats["random_trial_treated_hiv"] += 1
        if self.suitable:
            stats["random_trial_suitable"] += 1

update_pop(model) classmethod

Update the feature for the entire population (class method).

Initialize a random trial in the population if time is the random trial start time.

Parameters:

Name Type Description Default
model model.TITAN

the instance of TITAN currently being run

required
Source code in titan/features/random_trial.py
@classmethod
def update_pop(cls, model: "model.TITAN"):
    """
    Update the feature for the entire population (class method).

    Initialize a random trial in the population if time is the random trial start time.

    args:
        model: the instance of TITAN currently being run
    """
    rt_params = model.params.random_trial

    if not model.time == rt_params.start_time:
        return

    assert (
        model.params.model.network.enable
    ), "Network must be enabled for random trial"

    logging.info(f"Starting random trial ({rt_params.choice})")
    components = model.pop.connected_components()

    # set up helper methods based on params
    if rt_params.treatment == "prep":
        assert (
            model.params.features.prep
        ), "Prep feature must be enabled to use the prep random trial treatment"
        treat = treat_prep
        suitable = suitable_prep
    elif rt_params.treatment == "knowledge":
        assert (
            model.params.exposures.knowledge
        ), "Knowledge exposure must be enabled to use the knowledge random trial treatment"
        treat = treat_knowledge
        suitable = suitable_knowledge

    total_nodes = 0
    logging.info(
        f"Number of components {len([1 for comp in components if comp.number_of_nodes()])}",
    )
    for comp in components:
        total_nodes += comp.number_of_nodes()
        if model.run_random.random() < rt_params.prob:
            # Component selected as treatment pod!
            for agent in comp.nodes:
                agent.random_trial.active = True

            # treat all agents
            if rt_params.choice == "all":
                for agent in comp.nodes():
                    if suitable(agent, model):
                        treat(agent, model)
                        agent.random_trial.suitable = True
                        agent.random_trial.treated = True

            # chose an agent central to the component
            elif rt_params.choice == "eigenvector":
                centrality = nx.algorithms.centrality.eigenvector_centrality(comp)
                assert len(centrality) >= 1, "Empty centrality"
                ordered_centrality = sorted(centrality, key=centrality.get)

                # find the most central suitable agent, or if none, use most central
                intervention_agent = ordered_centrality[0]
                for agent in ordered_centrality:
                    if suitable(agent, model):
                        intervention_agent = agent
                        intervention_agent.random_trial.suitable = True
                        break

                intervention_agent.random_trial.treated = True
                treat(intervention_agent, model)

            # chose an agent that is a bridge in the network
            elif rt_params.choice == "bridge":
                # list all edges that are bridges
                all_bridges = list(nx.bridges(comp))
                suitable_agents = [
                    agent
                    for agents in all_bridges
                    for agent in agents
                    if suitable(agent, model)
                ]  # all suitable agents in bridges

                chosen_agent = utils.safe_random_choice(
                    suitable_agents, model.run_random
                )  # select change agent
                if chosen_agent is not None:
                    chosen_agent.random_trial.suitable = True  # type: ignore[attr-defined]

                else:  # if no suitable agents, mark a non-suitable agent
                    chosen_agent = utils.safe_random_choice(
                        list(comp.nodes), model.run_random
                    )

                chosen_agent.random_trial.treated = True  # type: ignore[attr-defined]
                treat(chosen_agent, model)

            # chose an agent from the component at random
            elif rt_params.choice == "random":
                suitable_agents = [
                    agent for agent in comp.nodes if suitable(agent, model)
                ]

                # if there are agents who meet eligibility criteria,
                # select one randomly
                chosen_agent = utils.safe_random_choice(
                    suitable_agents, model.run_random
                )

                if chosen_agent is not None:
                    chosen_agent.random_trial.suitable = True
                else:  # if no suitable agents, mark a non-suitable agent
                    chosen_agent = utils.safe_random_choice(
                        list(comp.nodes), model.run_random
                    )

                chosen_agent.random_trial.treated = True  # type: ignore[attr-defined]
                treat(chosen_agent, model)

    logging.info(f"Total agents in trial: {total_nodes}")