abstochkin.het_calcs

Some functions for calculating metrics of population heterogeneity.

  1""" Some functions for calculating metrics of population heterogeneity. """
  2
  3#  Copyright (c) 2024-2025, Alex Plakantonakis.
  4#
  5#  This program is free software: you can redistribute it and/or modify
  6#  it under the terms of the GNU General Public License as published by
  7#  the Free Software Foundation, either version 3 of the License, or
  8#  (at your option) any later version.
  9#
 10#  This program is distributed in the hope that it will be useful,
 11#  but WITHOUT ANY WARRANTY; without even the implied warranty of
 12#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13#  GNU General Public License for more details.
 14#
 15#  You should have received a copy of the GNU General Public License
 16#  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 17
 18from collections import Counter
 19
 20from numpy import log, ndarray
 21
 22from .process import Process, ReversibleProcess, MichaelisMentenProcess, \
 23    RegulatedProcess, RegulatedMichaelisMentenProcess
 24
 25ProcessClasses = Process | ReversibleProcess | MichaelisMentenProcess | RegulatedProcess | RegulatedMichaelisMentenProcess
 26
 27
 28def get_het_processes(processes: list[ProcessClasses, ...]) -> list[ProcessClasses, ...]:
 29    """
 30    Filter the heterogeneous processes from a given list of processes.
 31    A process is heterogeneous if any of its parameters are defined as such.
 32    """
 33    het_procs = list()
 34
 35    for proc in processes:
 36        if isinstance(proc, MichaelisMentenProcess):
 37            if proc.is_heterogeneous or proc.is_heterogeneous_Km:
 38                het_procs.append(proc)
 39        elif isinstance(proc, RegulatedMichaelisMentenProcess):
 40            if proc.is_heterogeneous or proc.is_heterogeneous_Km:
 41                het_procs.append(proc)
 42            else:
 43                if isinstance(proc.regulating_species, list):  # multiple regulators
 44                    if sum(proc.is_heterogeneous_K50) > 0:
 45                        het_procs.append(proc)
 46                elif isinstance(proc.regulating_species, str):  # only one regulator
 47                    if proc.is_heterogeneous_K50:
 48                        het_procs.append(proc)
 49        elif isinstance(proc, RegulatedProcess):
 50            if proc.is_heterogeneous:
 51                het_procs.append(proc)
 52            else:
 53                if isinstance(proc.regulating_species, list):  # multiple regulators
 54                    if sum(proc.is_heterogeneous_K50) > 0:
 55                        het_procs.append(proc)
 56                elif isinstance(proc.regulating_species, str):  # only one regulator
 57                    if proc.is_heterogeneous_K50:
 58                        het_procs.append(proc)
 59        elif isinstance(proc, ReversibleProcess):
 60            if proc.is_heterogeneous or proc.is_heterogeneous_rev:
 61                het_procs.append(proc)
 62        else:  # isinstance(proc, Process)
 63            if proc.is_heterogeneous:
 64                het_procs.append(proc)
 65
 66    return het_procs
 67
 68
 69def richness(arr: list | ndarray) -> int:
 70    """
 71    Calculate the species richness, or how many subspecies
 72    a species population comprises.
 73    """
 74    return len(Counter(arr))
 75
 76
 77def idx_het(arr: list | ndarray) -> float:
 78    """
 79    Calculate the Index of Heterogeneity (ψ), defined as the probability
 80    that two randomly chosen agents (without replacement) from a species
 81    population belong to different subspecies.
 82
 83    - A homogeneous population returns 0.
 84    - A heterogeneous population with two distinct subspecies of equal
 85      fractional abundance (χ=0.5) *approaches* 0.5 as the population
 86      size increases.
 87    - A fully heterogeneous population returns 1.
 88    """
 89    ck = Counter(arr)  # counter of k values
 90    n_tot = len(arr)  # total number of entries in sequence `arr`
 91
 92    s = 0
 93    for v in ck.values():
 94        s += v * (v - 1)
 95
 96    try:
 97        psi = 1 - s / (n_tot * (n_tot - 1))
 98    except ZeroDivisionError:
 99        psi = 0
100
101    return psi
102
103
104def info_het(arr: list | ndarray) -> float:
105    """
106    Information-theoretic measure of population heterogeneity.
107
108    - A homogeneous population returns 0.
109    - A heterogeneous population with two distinct subspecies of equal
110      fractional abundance (χ=0.5) returns ln(2). Note that this is
111      true regardless of the population size.
112    - For a fully heterogeneous population, the measure increases
113      with population size and has no upper limit.
114    """
115    ck = Counter(arr)  # counter of k values
116    n_tot = len(arr)  # total number of agents
117
118    s = 0
119
120    for k, v in ck.items():
121        chi = v / n_tot
122        s -= chi * log(chi)
123
124    return s
29def get_het_processes(processes: list[ProcessClasses, ...]) -> list[ProcessClasses, ...]:
30    """
31    Filter the heterogeneous processes from a given list of processes.
32    A process is heterogeneous if any of its parameters are defined as such.
33    """
34    het_procs = list()
35
36    for proc in processes:
37        if isinstance(proc, MichaelisMentenProcess):
38            if proc.is_heterogeneous or proc.is_heterogeneous_Km:
39                het_procs.append(proc)
40        elif isinstance(proc, RegulatedMichaelisMentenProcess):
41            if proc.is_heterogeneous or proc.is_heterogeneous_Km:
42                het_procs.append(proc)
43            else:
44                if isinstance(proc.regulating_species, list):  # multiple regulators
45                    if sum(proc.is_heterogeneous_K50) > 0:
46                        het_procs.append(proc)
47                elif isinstance(proc.regulating_species, str):  # only one regulator
48                    if proc.is_heterogeneous_K50:
49                        het_procs.append(proc)
50        elif isinstance(proc, RegulatedProcess):
51            if proc.is_heterogeneous:
52                het_procs.append(proc)
53            else:
54                if isinstance(proc.regulating_species, list):  # multiple regulators
55                    if sum(proc.is_heterogeneous_K50) > 0:
56                        het_procs.append(proc)
57                elif isinstance(proc.regulating_species, str):  # only one regulator
58                    if proc.is_heterogeneous_K50:
59                        het_procs.append(proc)
60        elif isinstance(proc, ReversibleProcess):
61            if proc.is_heterogeneous or proc.is_heterogeneous_rev:
62                het_procs.append(proc)
63        else:  # isinstance(proc, Process)
64            if proc.is_heterogeneous:
65                het_procs.append(proc)
66
67    return het_procs

Filter the heterogeneous processes from a given list of processes. A process is heterogeneous if any of its parameters are defined as such.

def richness(arr: list | numpy.ndarray) -> int:
70def richness(arr: list | ndarray) -> int:
71    """
72    Calculate the species richness, or how many subspecies
73    a species population comprises.
74    """
75    return len(Counter(arr))

Calculate the species richness, or how many subspecies a species population comprises.

def idx_het(arr: list | numpy.ndarray) -> float:
 78def idx_het(arr: list | ndarray) -> float:
 79    """
 80    Calculate the Index of Heterogeneity (ψ), defined as the probability
 81    that two randomly chosen agents (without replacement) from a species
 82    population belong to different subspecies.
 83
 84    - A homogeneous population returns 0.
 85    - A heterogeneous population with two distinct subspecies of equal
 86      fractional abundance (χ=0.5) *approaches* 0.5 as the population
 87      size increases.
 88    - A fully heterogeneous population returns 1.
 89    """
 90    ck = Counter(arr)  # counter of k values
 91    n_tot = len(arr)  # total number of entries in sequence `arr`
 92
 93    s = 0
 94    for v in ck.values():
 95        s += v * (v - 1)
 96
 97    try:
 98        psi = 1 - s / (n_tot * (n_tot - 1))
 99    except ZeroDivisionError:
100        psi = 0
101
102    return psi

Calculate the Index of Heterogeneity (ψ), defined as the probability that two randomly chosen agents (without replacement) from a species population belong to different subspecies.

  • A homogeneous population returns 0.
  • A heterogeneous population with two distinct subspecies of equal fractional abundance (χ=0.5) approaches 0.5 as the population size increases.
  • A fully heterogeneous population returns 1.
def info_het(arr: list | numpy.ndarray) -> float:
105def info_het(arr: list | ndarray) -> float:
106    """
107    Information-theoretic measure of population heterogeneity.
108
109    - A homogeneous population returns 0.
110    - A heterogeneous population with two distinct subspecies of equal
111      fractional abundance (χ=0.5) returns ln(2). Note that this is
112      true regardless of the population size.
113    - For a fully heterogeneous population, the measure increases
114      with population size and has no upper limit.
115    """
116    ck = Counter(arr)  # counter of k values
117    n_tot = len(arr)  # total number of agents
118
119    s = 0
120
121    for k, v in ck.items():
122        chi = v / n_tot
123        s -= chi * log(chi)
124
125    return s

Information-theoretic measure of population heterogeneity.

  • A homogeneous population returns 0.
  • A heterogeneous population with two distinct subspecies of equal fractional abundance (χ=0.5) returns ln(2). Note that this is true regardless of the population size.
  • For a fully heterogeneous population, the measure increases with population size and has no upper limit.