Skip to main content

internals.arrays

internals.arrays

Arrays of agents’ values: states such as age or infection status, and the UIDs that index them

A state holds a value for every agent, and reads only the active ones, so code can use it like a NumPy array as agents are born and die. A few classes here stand in for them when a run has groups of people rather than agents (GroupAges, GroupAlive).

Used with: agents and compartments; declared and coded modules

Classes

Name Description
Arr Store a state of the agents (e.g. age, infection status, etc.) as an array.
BaseArr An object that acts exactly like a NumPy array, except stores the values in self.values.
BoolArr Subclass of ss.Arr with defaults for booleans.
BoolState A boolean array being used as a state.
CatArr A categorical state: one of a list of categories for each agent (e.g. a district, or a wealth quintile), read, compared and set by category
FloatArr Subclass of ss.Arr with defaults for floats.
GroupAges people.age in a compartmental run: one row per group, at the middle of its age group
GroupAlive people.alive in a run without agents: one row per group of people, not one per person
IndexArr A special class of Arr used for UIDs and RNG IDs; not to be used as a general integer array (for that, use IntArr)
IntArr Subclass of ss.Arr with defaults for ints.
TimeArr Times, as step numbers of the module’s timeline: when something happened or is due for each agent, e.g. a declared disease’s ti_X (when each entered state X) and ti_exit_X, as agents, and people.ti_dead
UIDArr The UIDs of all agents (people.uid), which take no memory: a UID is the agent’s index, so raw is made when read
Unkept A declared disease’s time of entering a state (ti_X) that is not kept, since the engine does not need it (see ss.Sim(keep_times=True))
Watch The link from a state to the engine that keeps count of who is in it (BoolState.watch), which is told of
uids Class to specify that integers should be interpreted as UIDs.

Arr

internals.arrays.Arr(
    name=None,
    dtype=None,
    default=None,
    nan=None,
    label=None,
    raw=None,
    skip_init=False,
    people=None,
    mock=None,
    columns=None,
    shared=False,
)

Store a state of the agents (e.g. age, infection status, etc.) as an array.

In practice, Arr objects can be used interchangeably with NumPy arrays. They have two main data interfaces: Arr.raw contains the “raw”, underlying NumPy array of the data. Arr.values contains the “active” values, which usually corresponds to agents who are alive.

By default, operations are performed on active agents only (specified by Arr.auids, which is a pointer to sim.people.auids). For example, sim.people.age.mean() will only use the ages of active agents. Thus, sim.people.age.mean() is equal to sim.people.age.values.mean(), not sim.people.age.raw.mean().

Arr.raw includes agents who have died, and also has slots that are not people yet: room at the end for agents born later, which hold the array’s nan (NaN for an ss.FloatArr, not its default). So np.sum(arr.raw) can be NaN for a float array; to sum, count, or average over the people, use the array itself (arr.sum(), arr.mean()) or arr.values, and to read particular agents, index by UID (arr[uids]).

If indexing by an int, integer array, or ss.uids object, Arr.raw is used (i.e. the ints are treated as UIDs). If indexing by a full slice ([:]) or a boolean array, Arr.values is used. Arr objects can’t be directly indexed by a list, as this would be ambiguous about whether values or raw is intended. For example, if there are 1000 people in a simulation and 100 of them have died, sim.people.age[999] and sim.people.age[ss.uids(999)] both return the age of the agent with UID 999, whereas sim.people.age.values[899] returns the age of the last active agent.

Note on terminology: the term “states” is often used to refer to all ss.Arr objects, (e.g. in module.define_states()), whether or not they are BoolStates.

Parameters

Name Type Description Default
name str The name for the state (also used as the dictionary key, so should not have spaces etc.) None
dtype class The dtype to use for this instance (if None, infer from raw or default; str is stored as object) None
default any

Specify default value for new agents. This can be:

  • A scalar with the same dtype (or castable to the same dtype) as the Arr;
  • A callable, with a single argument for the number of values to produce;
  • An ss.Dist instance.
None
nan any the value to use to represent NaN (not a number); also used as the default value if not supplied (if None, inferred from the dtype, e.g. int_nan for integers) None
label str The human-readable name for the state None
raw array If provided, initialize the array with these raw values None
skip_init bool Whether to skip initialization with the People object (used for uid and slot states) False
people ss.People Optionally specify an initialized People object, used to construct temporary Arr instances None
mock int if provided, create a mock People object (of length mock, unless raw is provided) to initialize the array (for debugging purposes) None
columns int / list if provided, create a 2D array with this many columns (e.g. one per variant), or a list of column names (e.g. ['wildtype', 'mutant']) None
shared bool / Arr if True, the array takes no memory per agent until a value is set (other than by its default, which must be a scalar): until then every agent has the default, and raw is a read-only view of it; if an Arr, the same with that array’s values (as people.slot reads people.uid) False

For 2D arrays (i.e. if columns is supplied), arr[uids] returns an array of shape (len(uids), ncols), arr[uids, v] or arr[:, v] returns column v, and arr.col(v) returns column v as a 1D Arr. If the columns are named, v can also be a name, e.g. arr.col('mutant'), arr['mutant'], or arr.mutant. A scalar default fills every column, and a callable default should return an array of shape (n, ncols). Arrays with more dimensions are also supported via a tuple, e.g. columns=(2,3) gives a raw array of shape (len_tot, 2, 3).

Examples

# Create a standalone Arr for quick testing
age = ss.Arr('age', default=0, mock=5) # 5 = length if not supplying a real People object
age[:] = [20, 30, 40, 50, 60]
age.mean()  # Returns 40.0

# Use within a simulation
sim = ss.Sim(n_agents=100).init()
sim.people.age.mean()  # Mean age of active agents

# Create a 2D array, e.g. immunity to each of 3 variants
imm = ss.FloatArr('imm', default=0, columns=3, mock=5)
imm[ss.uids([0, 1]), 2] = 0.8 # Set immunity to variant 2 for agents 0 and 1
imm[ss.uids([0, 1])] # Returns an array of shape (2, 3)
(imm.col(2) > 0.5).uids  # Returns ss.uids([0, 1])

# Create a 2D array with named columns
imm = ss.FloatArr('imm', default=0, columns=['wildtype', 'mutant'], mock=5)
imm[ss.uids([0, 1]), 'mutant'] = 0.8 # Same as imm[ss.uids([0, 1]), 1] = 0.8
(imm.col('mutant') > 0.5).uids  # Returns ss.uids([0, 1])

Attributes

Name Description
auids Link to the indices of active agents – sim.people.auids
isnan Return BoolArr for NaN values
notnan Return BoolArr for non-NaN values
values Return the values of the active agents

Methods

Name Description
asnew Duplicate and copy (rather than link) data, optionally resetting the array
astype Convert the Arr type, e.g. arr.astype(ss.IntArr); other types (e.g. int) return a NumPy array
col Return column v of a 2D array as a 1D Arr, e.g. (imm.col(2) > 0.5).uids or imm.col('mutant')
convert Check if an object is an array-like, and convert it to this array’s type if so.
count Count the number of nonzero (truthy) values among active agents (for 2D arrays, use axis=0 to count each column)
false Reverse of true(); return UIDs of falsy values
grow Add new agents to an Arr
init_vals Actually populate the initial values and mark as initialized; only to be used on initialization
isin Return a BoolArr that is True wherever the array’s value is in values.
link_people Link a People object to this state, to access auids
own Give an array that has no memory of its own yet (see shared in ss.Arr) its own values, before one is set
set Set the values for the specified UIDs
set_nan Shortcut function to set values to NaN
shared_raw The read-only values of an array that has no memory of its own yet (see shared in ss.Arr): its default, or the values of the array it reads
to_json Export to JSON
true Efficiently convert truthy values to UIDs
asnew
internals.arrays.Arr.asnew(arr=None, cls=None, name=None, copy=None)

Duplicate and copy (rather than link) data, optionally resetting the array

Parameters
Name Type Description Default
arr array the values of the new array; if None, a copy of the active values None
cls type the class of the new array; if None, the same class None
name str the name of the new array None
copy bool whether to copy arr; if None, copy if arr is None or is a different size than the array None
astype
internals.arrays.Arr.astype(cls, copy=False)

Convert the Arr type, e.g. arr.astype(ss.IntArr); other types (e.g. int) return a NumPy array

Parameters
Name Type Description Default
cls type the class to convert to, e.g. ss.IntArr, or a NumPy type such as int required
copy bool whether to copy the values, for an ss.Arr class False
col
internals.arrays.Arr.col(v)

Return column v of a 2D array as a 1D Arr, e.g. (imm.col(2) > 0.5).uids or imm.col('mutant')

For arrays with more dimensions, returns an Arr with one fewer dimension. The new Arr is a view, so setting its values also changes the original array. It is not linked to People, so should not be stored (since it will not grow).

Parameters
Name Type Description Default
v int / str the column, by position or by name required
convert
internals.arrays.Arr.convert(obj, copy=None)

Check if an object is an array-like, and convert it to this array’s type if so.

Parameters
Name Type Description Default
obj object to convert (BaseArr, np.ndarray, or raises TypeError) required
copy bool whether to copy the data; passed through to asnew() None
count
internals.arrays.Arr.count(axis=None)

Count the number of nonzero (truthy) values among active agents (for 2D arrays, use axis=0 to count each column)

Parameters
Name Type Description Default
axis int the axis to count along, for a 2D array; if None, count all values None
false
internals.arrays.Arr.false()

Reverse of true(); return UIDs of falsy values

grow
internals.arrays.Arr.grow(new_uids=None, new_vals=None)

Add new agents to an Arr

This method is normally only called via People.grow().

Parameters
Name Type Description Default
new_uids array Numpy array of UIDs for the new agents being added None
new_vals array If provided, assign these state values to the new UIDs None
init_vals
internals.arrays.Arr.init_vals()

Actually populate the initial values and mark as initialized; only to be used on initialization

isin
internals.arrays.Arr.isin(values)

Return a BoolArr that is True wherever the array’s value is in values.

An Arr-aware analogue of numpy.isin that also picks the faster algorithm: values may be a scalar or any array-like of test values. For a small number of test values the equality comparisons are OR-ed together (which avoids the sort/hash overhead of np.isin and is faster for the common handful-of-categories case); for larger sets it falls back to np.isin. The result is a BoolArr, so .uids, [uids], and boolean combination all work.

Parameters
Name Type Description Default
values a scalar, or an array-like (list/tuple/array) of values to test against. required
Returns
Name Type Description
BoolArr True wherever the array’s value equals one of values.
Example
active = disease.state.isin([State.MILD, State.SEVERE]).uids
own
internals.arrays.Arr.own()

Give an array that has no memory of its own yet (see shared in ss.Arr) its own values, before one is set

set
internals.arrays.Arr.set(uids, new_vals=None)

Set the values for the specified UIDs

Parameters
Name Type Description Default
uids ss.uids the agents to set values for required
new_vals array the values; if None, use the default of the array None
set_nan
internals.arrays.Arr.set_nan(uids)

Shortcut function to set values to NaN

Parameters
Name Type Description Default
uids ss.uids the agents to set to NaN required
shared_raw
internals.arrays.Arr.shared_raw()

The read-only values of an array that has no memory of its own yet (see shared in ss.Arr): its default, or the values of the array it reads

to_json
internals.arrays.Arr.to_json()

Export to JSON

true
internals.arrays.Arr.true()

Efficiently convert truthy values to UIDs

BaseArr

internals.arrays.BaseArr(values, *args, **kwargs)

An object that acts exactly like a NumPy array, except stores the values in self.values.

It exists so that a state works in NumPy expressions while carrying what Starsim keeps with its values.

Methods

Name Description
asnew Duplicate and copy (rather than link) data.
convert Check if an object is an array, and convert if so
disp Full display of object
to_json Return a dictionary representation of the Arr
update Update the attributes, skipping None values and raising an error if extra keys are added
validate_columns Check that column names are unique strings that don’t clash with attributes, e.g. “low”
asnew
internals.arrays.BaseArr.asnew(values=None, cls=None, **kwargs)

Duplicate and copy (rather than link) data.

Parameters
Name Type Description Default
values array replacement data; if None, copies existing values None
cls type class to use for the new instance; defaults to the current class None
kwargs additional attributes to set on the new instance
convert
internals.arrays.BaseArr.convert(obj)

Check if an object is an array, and convert if so

disp
internals.arrays.BaseArr.disp()

Full display of object

to_json
internals.arrays.BaseArr.to_json()

Return a dictionary representation of the Arr

update
internals.arrays.BaseArr.update(
    skip_none=True,
    overwrite=True,
    force=False,
    **kwargs,
)

Update the attributes, skipping None values and raising an error if extra keys are added

validate_columns
internals.arrays.BaseArr.validate_columns()

Check that column names are unique strings that don’t clash with attributes, e.g. “low”

BoolArr

internals.arrays.BoolArr(name=None, **kwargs)

Subclass of ss.Arr with defaults for booleans.

It exists so that a flag can be defined by name alone, and adds what is useful for booleans, such as .uids.

Examples

# Create a standalone BoolArr
infected = ss.BoolArr('infected', mock=5)
infected[ss.uids([0, 2, 4])] = True
infected.count()  # Returns 3
infected.uids     # Returns ss.uids([0, 2, 4])

Attributes

Name Description
frac The fraction of the people alive it is True for now (n over people.n), as (sir.I \| sir.R).frac gives it under every solver
isnan Always False for BoolArr (booleans cannot be NaN).
n The number of people it is True for now, each counted by their weight and the sim’s scale, as (sir.I \| sir.R).n counts them under every solver (count() is the number of agents)
notnan Always True for BoolArr (booleans cannot be NaN).
uids Alias to Arr.true

Methods

Name Description
split Return UIDs of values that are true and false as separate arrays
split
internals.arrays.BoolArr.split()

Return UIDs of values that are true and false as separate arrays

BoolState

internals.arrays.BoolState(name=None, **kwargs)

A boolean array being used as a state.

Although functionally identical to BoolArr, a BoolState is handled differently in terms of automation: specifically, results are automatically generated from a BoolState (but not a BoolArr).

BoolStates are typically used to keep track of externally-facing variables (e.g. disease.susceptible), while BoolArrs can be used to keep track of internal ones (e.g. disease.has_immunity). A BoolState named “susceptible” will automatically generate a result named “susceptible” (the number susceptible), for example.

Note on terminology: the term “states” is often used to refer to all ss.Arr objects, (e.g. in module.define_states()), whether or not they are BoolStates.

Attributes

Name Description
n The number of people in the state: its agents, each counted by the people it stands for (its weight and the sim’s scale), as I.n in a rate counts them, and as a compartmental run’s state gives it; count() is the number of agents
stays For a state of a declared disease run as agents: how long each person who has been through it stayed there, e.g.
time_in For a state of a declared disease run as agents: the time each person in it (or leaving it) has spent there, by

Methods

Name Description
asnew As Arr.asnew(); a copy whose values are not the state’s own is not counted (see watch)
grow Add new agents (see Arr.grow()); they were not counted before, so whatever keeps count of the state counts those who start in it
set Set the values for the specified UIDs, telling whatever keeps count of the state (see __setitem__())
asnew
internals.arrays.BoolState.asnew(arr=None, cls=None, name=None, copy=None)

As Arr.asnew(); a copy whose values are not the state’s own is not counted (see watch)

Parameters
Name Type Description Default
arr array the values of the new array; if None, a copy of the active values None
cls type the class of the new array; if None, the same class None
name str the name of the new array None
copy bool whether to copy arr; see Arr.asnew() None
grow
internals.arrays.BoolState.grow(new_uids=None, new_vals=None)

Add new agents (see Arr.grow()); they were not counted before, so whatever keeps count of the state counts those who start in it

Parameters
Name Type Description Default
new_uids ss.uids the UIDs of the new agents None
new_vals array their values; if None, use the default of the array None
set
internals.arrays.BoolState.set(uids, new_vals=None)

Set the values for the specified UIDs, telling whatever keeps count of the state (see __setitem__())

Parameters
Name Type Description Default
uids ss.uids the agents to set values for required
new_vals array the values; if None, use the default of the array None

CatArr

internals.arrays.CatArr(name=None, categories=None, default=None, **kwargs)

A categorical state: one of a list of categories for each agent (e.g. a district, or a wealth quintile), read, compared and set by category

A category can be used wherever a value can, e.g. arr == 'urban', arr.isin(['urban', 'periurban']), and arr[uids] = 'urban'. Each category is stored as its index in categories (its code), so it is as fast as an ss.IntArr, and an integer given is a code, as arr[uids] returns (so arr[children] = arr[mothers] works). Integer categories (e.g. wealth quintiles 1 to 5, or the members of an IntEnum) are stored as they are, so arr == 3 is category 3. The categories can be an enum, e.g. ss.CatArr('state', categories=State).

Parameters

Name Type Description Default
name str the name of the state None
categories list / Enum the categories, e.g. ['urban', 'rural'], [1, 2, 3, 4, 5] (integers, or none of them), or an enum None
default any the value for new agents: a category, or a callable or ss.Dist returning categories or codes (e.g. ss.choice(a=categories, p=[0.3, 0.7])) None
kwargs dict passed to ss.Arr

Examples

loc = ss.CatArr('loc', categories=['urban', 'rural'], default='rural', mock=4)
loc[ss.uids([0, 1])] = 'urban'
(loc == 'urban').uids  # ss.uids([0, 1])
loc.labels()           # array(['urban', 'urban', 'rural', 'rural'], dtype=object)

Methods

Name Description
code The code of a category, or the codes of an array of them; integers (codes, or integer categories) are returned unchanged
index The position of each agent’s category in categories (by default, for the active agents), or -1 if not set (as pandas’ Categorical.codes), e.g. to count by category or look up a value for each
labels The categories of the agents given (by default, the active agents), with None for those not set
code
internals.arrays.CatArr.code(value)

The code of a category, or the codes of an array of them; integers (codes, or integer categories) are returned unchanged

index
internals.arrays.CatArr.index(uids=None)

The position of each agent’s category in categories (by default, for the active agents), or -1 if not set (as pandas’ Categorical.codes), e.g. to count by category or look up a value for each

labels
internals.arrays.CatArr.labels(uids=None)

The categories of the agents given (by default, the active agents), with None for those not set

FloatArr

internals.arrays.FloatArr(name=None, **kwargs)

Subclass of ss.Arr with defaults for floats.

It exists so that a state can be defined by name alone, e.g. ss.FloatArr('cd4').

GroupAges

internals.arrays.GroupAges(name=None, **kwargs)

people.age in a compartmental run: one row per group, at the middle of its age group

Comparing it with an age inside a group (sim.people.age > 50 with groups [0, 15, 65]) raises, since the answer would be the same for everyone in the group; an edge works.

GroupAlive

internals.arrays.GroupAlive(name=None, **kwargs)

people.alive in a run without agents: one row per group of people, not one per person

Counting it (.sum(), .count()) counts agents, which a run without agents does not have, so these raise, as do .uids and .true(), saying what to use instead: the number of people alive under every solver is people.n (or people.alive.n).

Attributes

Name Description
n The number of people alive: the people in the compartments (the same in every declared disease), or with none, the population

IndexArr

internals.arrays.IndexArr(name=None, label=None, shared=False)

A special class of Arr used for UIDs and RNG IDs; not to be used as a general integer array (for that, use IntArr)

The values are stored as 32-bit integers, to save memory (so there can be up to 2^31 agents), and read as UIDs (ss.uids, which keep 32-bit integers as they are).

Attributes

Name Description
raw The stored values; while they are those of people.uid (see shared in ss.Arr), the UIDs, made when read
uids Alias to self.values, to allow Arr.uids like BoolArr
values The values of the active agents, as UIDs

Methods

Name Description
grow Change the size of the array
grow
internals.arrays.IndexArr.grow(new_uids=None, new_vals=None)

Change the size of the array

IntArr

internals.arrays.IntArr(name=None, **kwargs)

Subclass of ss.Arr with defaults for ints.

It exists so that a state can be defined by name alone, e.g. ss.IntArr('n_doses').

Note: Because integer arrays do not handle NaN values natively, users are recommended to use ss.FloatArr() in most cases instead.

TimeArr

internals.arrays.TimeArr(name=None, **kwargs)

Times, as step numbers of the module’s timeline: when something happened or is due for each agent, e.g. a declared disease’s ti_X (when each entered state X) and ti_exit_X, as agents, and people.ti_dead

Stored as float32 step numbers, NaN for “never” (not set), so ti_I > 0 and (sim.ti - ti_I)*sim.t.dt give the right answer. due() gives whose time has come, with a time between steps rounded up or down at random in proportion (by each agent’s phase, as the agent engine rounds a dwell time: see ss.People.phase()), so it keeps its mean; isclose(sim.ti) gives whose time is taken at this step, and .years converts them to years. == and != raise, since a time between steps never equals a step: eq() is exact equality. For plain arithmetic on the times, compare them: e.g. ti_x < sim.ti + 0.5 is due at the nearest step, and ti_x <= sim.ti at the first step at or after it.

A declared state’s time of entering (ti_X) reads as the index of the step the move happened in, whatever kind of move it was (a progression at the step’s start, an infection by contact at its end), so ti_X.isclose(sim.ti) is “entered this step”. The engine keeps its own time of the move, which .raw gives (see shift). So the difference of two of them is not always a duration: an infection by contact happens at the end of its step and a scheduled move at the start of its step, so ti_I - ti_E of an SEIR reads one step longer than the latent period was. For the time people spent in a state, use the state’s stays (e.g. np.mean(seir.E.stays), the mean latent period), which is built from the engine’s times.

Example

sim = ss.Sim(ss.SIR(), unit='day', stop=100, solver='abm').run()
sir = sim.diseases.sir
infected_today = sir.ti_I.isclose(sim.ti).uids
years = sir.ti_I.years # The year of infection of each agent (NaN if never infected)

# In a disease written as code
self.define_states(ss.BoolState('exposed'), ss.TimeArr('ti_infectious'))
self.ti_infectious[uids] = self.ti + dur # In steps
mask = self.exposed & self.ti_infectious.due() # Those whose time has come

Attributes

Name Description
years The values as years (e.g. 2020.5), with NaN where not set

Methods

Name Description
due Whose time has come by the current step, as a boolean array (e.g. mask = self.exposed & self.ti_infectious.due()): a time between steps is rounded up or down at random in proportion, by each agent’s phase (see ss.People.phase())
eq Whose time is exactly other, as a boolean array (== and != are refused: see __eq__())
isclose Whose time is taken at step other (e.g. self.ti_infectious.isclose(self.ti), those who became infectious this step, as due() counts them), as a boolean array
shifted Add the shift to some values read, by agent (all of them, or those of key)
due
internals.arrays.TimeArr.due()

Whose time has come by the current step, as a boolean array (e.g. mask = self.exposed & self.ti_infectious.due()): a time between steps is rounded up or down at random in proportion, by each agent’s phase (see ss.People.phase())

eq
internals.arrays.TimeArr.eq(other)

Whose time is exactly other, as a boolean array (== and != are refused: see __eq__())

Parameters
Name Type Description Default
other float / array the timestep or timesteps to compare with required
isclose
internals.arrays.TimeArr.isclose(other)

Whose time is taken at step other (e.g. self.ti_infectious.isclose(self.ti), those who became infectious this step, as due() counts them), as a boolean array

Parameters
Name Type Description Default
other int the timestep required
shifted
internals.arrays.TimeArr.shifted(vals, key=None)

Add the shift to some values read, by agent (all of them, or those of key)

UIDArr

internals.arrays.UIDArr(name=None, label=None, shared=False)

The UIDs of all agents (people.uid), which take no memory: a UID is the agent’s index, so raw is made when read

It is np.arange() up to the number of agents made, and -1 (not set) in the room for agents not yet made, as a stored IndexArr would be. Code that only needs its length reads len_used or len_tot instead.

Methods

Name Description
grow Add new agents, whose UIDs are the next ones
isin Which agents’ UIDs are in values (see ss.Arr.isin()): as a UID is the agent’s index, by setting those places, with no search
grow
internals.arrays.UIDArr.grow(new_uids=None, new_vals=None)

Add new agents, whose UIDs are the next ones

isin
internals.arrays.UIDArr.isin(values)

Which agents’ UIDs are in values (see ss.Arr.isin()): as a UID is the agent’s index, by setting those places, with no search

Unkept

internals.arrays.Unkept(name, mod)

A declared disease’s time of entering a state (ti_X) that is not kept, since the engine does not need it (see ss.Sim(keep_times=True))

Setting it does nothing, and reading it raises an AttributeError that says how to keep it. It takes no memory, and is not one of the module’s states.

Methods

Name Description
unkept The error for reading it
unkept
internals.arrays.Unkept.unkept()

The error for reading it

Watch

internals.arrays.Watch(engine)

The link from a state to the engine that keeps count of who is in it (BoolState.watch), which is told of every change to it (see ss.solvers.Agents.wrote()). It is never searched for distributions (see ss.dists.no_dists()), so the link does not change where they are found, which gives each its random numbers.

uids

internals.arrays.uids()

Class to specify that integers should be interpreted as UIDs.

It exists so that indexing an ss.Arr with these reads those agents by UID, not by position.

For all practical purposes, behaves like a NumPy integer array. However, has additional methods uids.concatenate(), uids.remove(), and uids.intersect() to simplify common UID operations.

The following operators are supported:

  • + / +=: if the RHS is a uids, concatenation (equivalent to uids.concatenate()); otherwise element-wise addition (e.g. uids([1,2]) + 1 → uids([2,3])). Concatenation preserves duplicates.
  • - / -=: set difference — equivalent to uids.remove(). Duplicate entries in the original will also be removed (uses np.setdiff1d internally).
  • | / |=: union (unique elements from both arrays). Note that duplicate entries in the original are removed (use + instead to keep them).
  • & / &=: intersection
  • ^ / ^=: symmetric difference

Note: because uids is a subclass of np.ndarray, set-like in-place operators (+= with a uids RHS, -=, |=, &=, ^=) must return a new array and rebind the variable. However, += with a scalar or array RHS modifies the array in-place and preserves id(self).

Examples

a = ss.uids([1, 2, 3])
b = ss.uids([3, 4, 5])
a + b   # Concatenate: uids([1, 2, 3, 3, 4, 5])
a | b   # Union:       uids([1, 2, 3, 4, 5])
a & b   # Intersect:   uids([3])
a - b   # Difference:  uids([1, 2])

Methods

Name Description
concatenate Concatenate uids into a single instance
intersect Keep only UIDs that are also present in the other array
remove Remove provided UIDs from current array
to_numpy Return a view as a standard NumPy array
union Return all UIDs present in either array, sorted, without repeats (as np.union1d() gives them, faster: see unique())
unique Return unique UIDs; equivalent to np.unique()
xor Return UIDs present in only one of the arrays
concatenate
internals.arrays.uids.concatenate(*args)

Concatenate uids into a single instance

Can be called as either an instance method or an unbound class method.

Parameters
Name Type Description Default
*args ss.uids the UIDs to join, as arrays or as one list of arrays; None is treated as empty
Examples
x.concatenate(y)               # instance method
x.concatenate(None)            # None is treated as empty
ss.uids.concatenate(x, y)      # unbound; identical to instance call
ss.uids.concatenate([x, y])    # single iterable of arrays
ss.uids.concatenate([None, x]) # None elements are filtered out
ss.uids.concatenate()          # returns empty ss.uids()
intersect
internals.arrays.uids.intersect(other, **kw)

Keep only UIDs that are also present in the other array

Parameters
Name Type Description Default
other ss.uids/ss.BoolArr the UIDs to keep, if they are also in this array required
**kw dict passed to np.intersect1d()
remove
internals.arrays.uids.remove(other, **kw)

Remove provided UIDs from current array

Parameters
Name Type Description Default
other ss.uids/ss.BoolArr the UIDs to remove required
**kw dict passed to np.setdiff1d()
to_numpy
internals.arrays.uids.to_numpy()

Return a view as a standard NumPy array

union
internals.arrays.uids.union(other)

Return all UIDs present in either array, sorted, without repeats (as np.union1d() gives them, faster: see unique())

Parameters
Name Type Description Default
other ss.uids/ss.BoolArr the UIDs to add required
unique
internals.arrays.uids.unique(return_index=False)

Return unique UIDs; equivalent to np.unique()

Parameters
Name Type Description Default
return_index bool whether to also return the position of the first time each UID appears False
xor
internals.arrays.uids.xor(other, **kw)

Return UIDs present in only one of the arrays

Parameters
Name Type Description Default
other ss.uids/ss.BoolArr the UIDs to compare with required
**kw dict passed to np.setxor1d()

Functions

Name Description
nb_count The number of active agents whose value is truthy (as np.count_nonzero()), without gathering the values first
nb_indexer Roughly 30% faster than NumPy for large numbers of indices (>10k)
nb_true Return the UIDs where bool(vals[k]) equals want (True for true(), False for false()).
nb_true_raw As nb_true(), reading each agent’s value from the full array, so the values of the active agents are not gathered first
np_indexer Much faster than Numba for small numbers of indices (<1k)

nb_count

internals.arrays.nb_count(auids, raw)

The number of active agents whose value is truthy (as np.count_nonzero()), without gathering the values first

nb_indexer

internals.arrays.nb_indexer(arr, inds)

Roughly 30% faster than NumPy for large numbers of indices (>10k)

NB: in some versions of Numba (>0.62), the explicit loop is much faster than array indexing.

nb_true

internals.arrays.nb_true(auids, vals, want)

Return the UIDs where bool(vals[k]) equals want (True for true(), False for false()).

~4-5x faster than auids[vals.astype(bool)] for large arrays: a single branchless pass with no intermediate mask allocation. vals[k] != 0 matches .astype(bool) for every dtype, including NaN floats (NaN != 0 is True, i.e. NaN is truthy), so results are identical.

nb_true_raw

internals.arrays.nb_true_raw(auids, raw, want)

As nb_true(), reading each agent’s value from the full array, so the values of the active agents are not gathered first

np_indexer

internals.arrays.np_indexer(arr, inds)

Much faster than Numba for small numbers of indices (<1k)