Source code for depo

"""Deposit (Depo) utility for tracking nodes held for a fixed duration.

This module provides the :class:`Depo` class used by policy objects to
place nodes into quarantine or isolation for a configurable number of
time-steps and to release them automatically when their sentence
expires.
"""

import numpy as np
import logging

[docs] class Depo: """Deposit object for holding nodes for a fixed number of time-steps. Used in policies to store nodes (e.g. quarantined individuals) for a given period of time. Internally maintains a counter array; each entry holds the remaining number of time-steps before that node is released. A value of zero means the node is not currently held. Args: size (int): Total number of nodes in the simulation (length of the internal counter array). """
[docs] def __init__(self, size): """Initialise the deposit with a counter array of zeros. Args: size (int): Number of nodes (length of the counter array). """ self.depo = np.zeros(size, dtype="uint8")
@property def num_of_prisoners(self): """Return the number of nodes currently held in the deposit. Returns: int: Count of nodes whose remaining time is greater than zero. """ return (self.depo > 0).sum()
[docs] def lock_up(self, nodes, duration=14, check_duplicate=False): """Place nodes into the deposit for a given duration. Args: nodes (list or numpy.ndarray): Node indices to lock up. duration (int or numpy.ndarray): Number of time-steps each node should remain in the deposit. May be a scalar applied to all nodes or an array of per-node values. Defaults to 14. check_duplicate (bool): If ``True``, nodes that are already in the deposit are silently ignored (their remaining time is not updated). If ``False`` (default), an assertion error is raised if any node is already held. Raises: AssertionError: If ``nodes`` is not a list or ``numpy.ndarray``, or if ``check_duplicate`` is ``False`` and any node is already in the deposit. """ assert isinstance(nodes, np.ndarray) or isinstance(nodes, list), f"real type {type(nodes)}" if len(nodes) > 0: assert check_duplicate or np.all(self.depo[nodes] == 0) if check_duplicate: nodes = np.array(nodes) zero_nodes = nodes[(self.depo[nodes] == 0).nonzero()[0]] self.depo[zero_nodes] = duration else: self.depo[nodes] = duration
[docs] def filter_locked(self, candidates): """Return the subset of candidates that are not yet in the deposit. Args: candidates (numpy.ndarray): Array of node indices to filter. Returns: numpy.ndarray: Subset of ``candidates`` whose deposit counter is currently zero (i.e. not locked up). """ if len(candidates) > 0: return candidates[self.depo[candidates]==0] else: return candidates
[docs] def filter_locked_bitmap(self, candidates): """Return a boolean mask indicating which candidates are not in the deposit. Args: candidates (numpy.ndarray): Array of node indices to check. Returns: numpy.ndarray: Boolean array of the same length as ``candidates``. Entry is ``True`` if the corresponding candidate is **not** currently in the deposit, ``False`` if it is already locked up. """ return self.depo[candidates] == 0
[docs] def tick_and_get_released(self): """Advance the deposit by one time-step and return newly released nodes. Decrements all non-zero counters by one. Nodes whose counter reaches zero are considered released. Returns: numpy.ndarray: Array of node indices that were released (i.e. had a counter of exactly 1 before the decrement). """ released = np.nonzero(self.depo == 1)[0] self.depo[self.depo >= 1] -= 1 return released
[docs] def is_locked(self, node_id): """Check whether a single node is currently held in the deposit. Args: node_id (int): Index of the node to check. Returns: bool: ``True`` if the node's remaining time is greater than zero, ``False`` otherwise. """ return self.depo[node_id] > 0