"""
Compute scores for communities, which permit ranking communities based on potential
for cooperation or competition, or based on added value in terms of scope or activated
reactions.
"""
from functools import cache
from cocomico import constants as cx
from cocomico.base import Biomolecule, Exchange, Seeds
from cocomico.community import Community
[docs]
@cache
def competition(community: Community, seeds: Seeds) -> tuple[float, dict[str, float]]:
"""
The competition potential of a community is calculated as the ratio of
polyopsonist metabolites to taxa.
"""
polyopsonist: dict[Biomolecule, int] = community.polyopsonist(seeds)
num_taxa = len(community.taxa)
score = float(sum(polyopsonist.values())) / num_taxa
metrics = {cx.COMP: score, cx.COMP_NUM: len(polyopsonist)}
return score, metrics
[docs]
@cache
def delta(community: Community, seeds: Seeds) -> tuple[float, dict[str, int]]:
"""
The delta score of a community is the difference between the number
of metabolites in the community scope and the sum of the individual
scopes.
"""
# Recall community.scope(seeds, choice): MetaboliteSet
community_scope = len(community.scope(seeds))
individual_scope = sum(
len(community.scope(seeds, choice=t)) for t in community.taxa
)
score = community_scope - individual_scope
metrics = {
cx.DELTA: score,
cx.DELTA_CSCOPE: community_scope,
cx.DELTA_ISCOPE: individual_scope,
}
return score, metrics
[docs]
@cache
def rho(community: Community, seeds: Seeds) -> tuple[float, dict[str, int]]:
"""
The rho score of a community is the difference between the number
of activated×taxon tuples in the community scope and the sum of the
individual activated×taxon tuples.
"""
# Recall community.activated(seeds, choice): set[Reaction]
# Recall Reaction = Tuple[str, Taxon]
community_active = len(community.activated(seeds))
individual_active = sum(
len(community.activated(seeds, choice=t)) for t in community.taxa
)
score = community_active - individual_active
metrics = {
cx.RHO: score,
cx.RHO_CACTIV: community_active,
cx.RHO_IACTIV: individual_active,
}
return score, metrics
[docs]
@cache
def cooperation(community: Community, seeds: Seeds) -> tuple[float, dict[str, float]]:
"""
The cooperation potential of a community is calculated from the metabolic
exchanges. It is the weighted sum, over all exchanged metabolites, of
the number of producers and consumers of that metabolite. The weight
is an exponentially decaying bonus.
"""
def weight(k: set) -> float:
"""Exponentially decaying weight, starting from 1, up to 2."""
n = len(k)
return 2 - (0.5 ** (n - 1)) if n >= 1 else 0
# The value for each metabolite key is a set of (producer,consumer) pairs.
exchanges: dict[Biomolecule, set[Exchange]] = community.exchange(seeds)
prodcons = [
sum(weight({pc[which] for pc in pairs}) for pairs in exchanges.values())
for which in [0, 1]
]
score = float(sum(prodcons))
metrics: dict[str, float] = {
cx.COOP: score,
cx.COOP_NUM: len(exchanges),
cx.COOP_PROD: prodcons[0],
cx.COOP_CONS: prodcons[1],
}
return score, metrics