Source code for pyphi.models.fmt
# models/fmt.py
"""Helper functions for formatting pretty representations of PyPhi models."""
from fractions import Fraction
from typing import Any
from pyphi import numerics
from pyphi.conf import config
# Unicode symbols
SMALL_PHI = "φ"
HORIZONTAL_BAR = "─"
LINE = "━"
ARROW_RIGHT = LINE * 2 + "▶"
EMPTY_SET = "∅"
NICE_DENOMINATORS = [*list(range(16)), 16, 32, 64, 128]
[docs]
def labels(
indices: tuple[int, ...], node_labels: object | None = None
) -> tuple[str, ...]:
"""Get the labels for a tuple of mechanism indices."""
if node_labels is None:
return tuple(map(str, indices))
return node_labels.indices2labels(indices) # type: ignore[attr-defined]
[docs]
def fmt_number(p: Any) -> str:
"""Format a number.
It will be printed as a fraction if the denominator isn't too big and as a
decimal otherwise.
If formatting fails, return the input unmodified.
"""
try:
formatted = format(p, f".{config.numerics.precision}f")
except (ValueError, TypeError):
return str(p)
if not config.infrastructure.print_fractions:
return formatted
fraction = Fraction(p)
nice = fraction.limit_denominator(128)
return (
str(nice)
if (
numerics.eq(float(fraction), float(nice))
and nice.denominator in NICE_DENOMINATORS
)
else formatted
)
[docs]
def fmt_nodes(nodes: tuple[int, ...], node_labels: object | None = None) -> str:
"""Format nodes, optionally with labels."""
return ",".join(labels(nodes, node_labels)) if nodes else EMPTY_SET
[docs]
def fmt_mechanism(indices: tuple[int, ...], node_labels: object | None = None) -> str:
"""Format a mechanism or purview."""
return "[" + fmt_nodes(indices, node_labels=node_labels) + "]"
[docs]
def fmt_part(part: object, node_labels: object | None = None) -> str:
"""Format a :class:`~pyphi.models.partitions.Part`.
The returned string looks like::
0,1
───
∅
"""
numer = fmt_nodes(part.mechanism, node_labels=node_labels) # type: ignore[attr-defined]
denom = fmt_nodes(part.purview, node_labels=node_labels) # type: ignore[attr-defined]
w = max(3, len(numer), len(denom))
divider = HORIZONTAL_BAR * w
return ("{numer:^{width}}\n{divider}\n{denom:^{width}}").format(
numer=numer, divider=divider, denom=denom, width=w
)
[docs]
def fmt_partition_arrow(
cut: object, direction: object | None = None, name: bool = True
) -> str:
"""Format a :class:`~pyphi.models.partitions.DirectedBipartition` as an
arrow expression.
The arrow always points from ``from_nodes`` to ``to_nodes`` — the
connections the cut severs (matching :meth:`removed_edges` and the
severed-connections grid). The causal ``direction`` the cut is evaluated
against is annotated textually.
"""
try:
if name:
name_str = cut.__class__.__name__ + " "
else:
name_str = ""
direction_str = (
"" if direction is None else f"({direction.name.lower()}) " # type: ignore[attr-defined]
)
from_nodes = fmt_mechanism(cut.from_nodes, cut.node_labels) # type: ignore[attr-defined]
to_nodes = fmt_mechanism(cut.to_nodes, cut.node_labels) # type: ignore[attr-defined]
symbol = ARROW_RIGHT if direction is None else LINE * 2 + "/ /" + ARROW_RIGHT
return f"{name_str}{direction_str}{from_nodes} {symbol} {to_nodes}"
except AttributeError:
return str(cut)
[docs]
def fmt_directed_joint_partition(cut: object) -> str:
"""Format a :class:`~pyphi.models.partitions.DirectedJointPartition`."""
return f"DirectedJointPartition {cut.direction}\n{cut.partition}" # type: ignore[attr-defined]
[docs]
def fmt_extended_purview(
extended_purview: Any, node_labels: object | None = None
) -> str:
"""Format an extended purview."""
if len(extended_purview) == 1:
return fmt_mechanism(extended_purview[0], node_labels=node_labels)
purviews = [
fmt_mechanism(purview, node_labels=node_labels) for purview in extended_purview
]
return "[" + ", ".join(purviews) + "]"
[docs]
def fmt_transition(t: object) -> str:
"""Format a :class:`~pyphi.actual.Transition`."""
cause = fmt_mechanism(t.cause_indices, t.node_labels) # type: ignore[attr-defined]
effect = fmt_mechanism(t.effect_indices, t.node_labels) # type: ignore[attr-defined]
return f"Transition({cause} {ARROW_RIGHT} {effect})"
[docs]
def state(state: tuple[int, ...]) -> str:
"""Format a state."""
return "(" + ",".join(map(str, state)) + ")"