Skip to content

Conversion and interop

ProbPipe converts between distribution representations through a registry of Converter classes. The from_distribution op tries registered converters in descending priority order and runs the first whose check() reports a feasible conversion. Built-ins cover:

  • ProbPipe-to-ProbPipe (same-class passthrough; cross-family moment-matching)
  • TFP ↔ ProbPipe (bidirectional)
  • scipy.stats ↔ ProbPipe (bidirectional, optional dependency)

For Record-side metadata interop — preserving xarray dims / coords or pandas index / columns through a NumericRecord round-trip — see the auxiliary-metadata registry instead. That's a separate, simpler registry; this page is only about distribution-to-distribution conversion.

Registry

converter_registry = ConverterRegistry() module-attribute

ConverterRegistry()

Global registry of distribution converters.

Converters are tried in descending priority order. The first converter whose check() returns feasible=True wins.

Source code in probpipe/converters/_registry.py
def __init__(self) -> None:
    self._converters: list[Converter] = []
    self._type_cache: dict[type, list[Converter]] = {}

register(converter)

Register a converter (invalidates the lookup cache).

Source code in probpipe/converters/_registry.py
def register(self, converter: Converter) -> None:
    """Register a converter (invalidates the lookup cache)."""
    self._converters.append(converter)
    self._converters.sort(key=lambda c: c.priority, reverse=True)
    self._type_cache.clear()

check(source, target_type)

Return conversion metadata for sourcetarget_type.

Tries converters in priority order; returns the first feasible result. Returns a non-feasible ConversionInfo if no converter can handle the pair.

Source code in probpipe/converters/_registry.py
def check(self, source: Any, target_type: type) -> ConversionInfo:
    """Return conversion metadata for *source* → *target_type*.

    Tries converters in priority order; returns the first feasible
    result.  Returns a non-feasible ``ConversionInfo`` if no
    converter can handle the pair.
    """
    for conv in self._find_converters(type(source)):
        info = conv.check(source, target_type)
        if info.feasible:
            return info
    return ConversionInfo(
        feasible=False,
        source_type=type(source),
        target_type=target_type,
        description="No converter found",
    )

convert(source, target_type, *, key=None, **kwargs)

Convert source to target_type using the best converter.

Raises TypeError if no converter can handle the pair.

Source code in probpipe/converters/_registry.py
def convert(
    self,
    source: Any,
    target_type: type,
    *,
    key: Any | None = None,
    **kwargs: Any,
) -> Any:
    """Convert *source* to *target_type* using the best converter.

    Raises ``TypeError`` if no converter can handle the pair.
    """
    for conv in self._find_converters(type(source)):
        info = conv.check(source, target_type)
        if info.feasible:
            return conv.convert(source, target_type, key=key, **kwargs)
    raise TypeError(
        f"No converter registered for "
        f"{type(source).__name__} -> {target_type.__name__}"
    )

is_distribution_type(obj)

Return True if obj is a recognized distribution-like object.

This includes any ProbPipe Distribution subclass as well as external distribution types (e.g., TFP, scipy.stats) for which a registered converter declares support.

Source code in probpipe/converters/_registry.py
def is_distribution_type(self, obj: Any) -> bool:
    """Return ``True`` if *obj* is a recognized distribution-like object.

    This includes any ProbPipe ``Distribution`` subclass as well as
    external distribution types (e.g., TFP, scipy.stats) for which
    a registered converter declares support.
    """
    from ..core.distribution import Distribution

    if isinstance(obj, Distribution):
        return True
    return any(
        isinstance(obj, tuple(c.source_types()))
        for c in self._converters
    )

Converter classes

Converter

Bases: ABC

Base class for distribution converters.

Subclasses declare which types they can convert between via source_types() and target_types(), provide a cheap check() probe, and implement convert() for the actual work.

priority property

Higher priority converters are tried first. Default 0.

source_types() abstractmethod

Types this converter can convert FROM.

Source code in probpipe/converters/_registry.py
@abstractmethod
def source_types(self) -> tuple[type, ...]:
    """Types this converter can convert FROM."""
    ...

target_types() abstractmethod

Types this converter can convert TO.

Source code in probpipe/converters/_registry.py
@abstractmethod
def target_types(self) -> tuple[type, ...]:
    """Types this converter can convert TO."""
    ...

check(source, target_type) abstractmethod

Inspect feasibility and cost without performing conversion.

Must be cheap (no sampling, no heavy computation).

Source code in probpipe/converters/_registry.py
@abstractmethod
def check(self, source: Any, target_type: type) -> ConversionInfo:
    """Inspect feasibility and cost without performing conversion.

    Must be cheap (no sampling, no heavy computation).
    """
    ...

convert(source, target_type, *, key=None, **kwargs) abstractmethod

Perform the actual conversion.

Returns an instance of target_type (or a compatible subclass).

Source code in probpipe/converters/_registry.py
@abstractmethod
def convert(self, source: Any, target_type: type, *, key: Any | None = None, **kwargs: Any) -> Any:
    """Perform the actual conversion.

    Returns an instance of *target_type* (or a compatible subclass).
    """
    ...

ConversionInfo(feasible, method=None, estimated_time=0.0, source_type=None, target_type=None, description='') dataclass

Metadata describing a potential conversion.

Returned by Converter.check and ConverterRegistry.check.

ConversionMethod

Bases: Enum

How a conversion is performed.

Built-in priorities

Priority Converter Role
200 ProtocolConverter Resolves protocol targets (e.g., SupportsLogProb) to a concrete type and delegates back to the registry
100 ProbPipeConverter ProbPipe-to-ProbPipe (same-class passthrough or cross-family moment-matching)
50 TFPConverter Bidirectional TFP ↔ ProbPipe
25 ScipyConverter Bidirectional scipy.stats ↔ ProbPipe (optional)

Higher priority is tried first.