Skip to content

dyce.h package reference

HableOpsMixin

A “mix-in” class providing arithmetic operations for implementers of the HableT protocol. The P class derives from this class.

Info

See HableT for notes on pronunciation.

__slots__: Union[str, Iterable[str]] special

__abs__(self: HableT) -> H special

Shorthand for operator.__abs__(self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __abs__(self: HableT) -> H:
    r"""
    Shorthand for ``#!python operator.__abs__(self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __abs__(self.h())

__add__(self: HableT, other: _OperandT) -> H special

Shorthand for operator.__add__(self.h(), other). See the h method.

Source code in dyce/h.py
@beartype
def __add__(self: HableT, other: _OperandT) -> H:
    r"""
    Shorthand for ``#!python operator.__add__(self.h(), other)``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __add__(self.h(), other)

__and__(self: HableT, other: Union[SupportsIntSCU, H, HableT]) -> H special

Shorthand for operator.__and__(self.h(), other). See the h method.

Source code in dyce/h.py
@beartype
def __and__(self: HableT, other: Union[SupportsIntSCU, H, HableT]) -> H:
    r"""
    Shorthand for ``#!python operator.__and__(self.h(), other)``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __and__(self.h(), other)

__floordiv__(self: HableT, other: _OperandT) -> H special

Shorthand for operator.__floordiv__(self.h(), other). See the h method.

Source code in dyce/h.py
@beartype
def __floordiv__(self: HableT, other: _OperandT) -> H:
    r"""
    Shorthand for ``#!python operator.__floordiv__(self.h(), other)``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __floordiv__(self.h(), other)

__invert__(self: HableT) -> H special

Shorthand for operator.__invert__(self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __invert__(self: HableT) -> H:
    r"""
    Shorthand for ``#!python operator.__invert__(self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __invert__(self.h())

__mod__(self: HableT, other: _OperandT) -> H special

Shorthand for operator.__mod__(self.h(), other). See the h method.

Source code in dyce/h.py
@beartype
def __mod__(self: HableT, other: _OperandT) -> H:
    r"""
    Shorthand for ``#!python operator.__mod__(self.h(), other)``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __mod__(self.h(), other)

__mul__(self: HableT, other: _OperandT) -> H special

Shorthand for operator.__mul__(self.h(), other). See the h method.

Source code in dyce/h.py
@beartype
def __mul__(self: HableT, other: _OperandT) -> H:
    r"""
    Shorthand for ``#!python operator.__mul__(self.h(), other)``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __mul__(self.h(), other)

__neg__(self: HableT) -> H special

Shorthand for operator.__neg__(self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __neg__(self: HableT) -> H:
    r"""
    Shorthand for ``#!python operator.__neg__(self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __neg__(self.h())

__or__(self: HableT, other: Union[SupportsIntSCU, H, HableT]) -> H special

Shorthand for operator.__or__(self.h(), other). See the h method.

Source code in dyce/h.py
@beartype
def __or__(self: HableT, other: Union[SupportsIntSCU, H, HableT]) -> H:
    r"""
    Shorthand for ``#!python operator.__or__(self.h(), other)``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __or__(self.h(), other)

__pos__(self: HableT) -> H special

Shorthand for operator.__pos__(self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __pos__(self: HableT) -> H:
    r"""
    Shorthand for ``#!python operator.__pos__(self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __pos__(self.h())

__pow__(self: HableT, other: _OperandT) -> H special

Shorthand for operator.__pow__(self.h(), other). See the h method.

Source code in dyce/h.py
@beartype
def __pow__(self: HableT, other: _OperandT) -> H:
    r"""
    Shorthand for ``#!python operator.__pow__(self.h(), other)``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __pow__(self.h(), other)

__radd__(self: HableT, other: RealLikeSCU) -> H special

Shorthand for operator.__add__(other, self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __radd__(self: HableT, other: RealLikeSCU) -> H:
    r"""
    Shorthand for ``#!python operator.__add__(other, self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __add__(other, self.h())

__rand__(self: HableT, other: SupportsIntSCU) -> H special

Shorthand for operator.__and__(other, self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __rand__(self: HableT, other: SupportsIntSCU) -> H:
    r"""
    Shorthand for ``#!python operator.__and__(other, self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __and__(other, self.h())

__rfloordiv__(self: HableT, other: RealLikeSCU) -> H special

Shorthand for operator.__floordiv__(other, self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __rfloordiv__(self: HableT, other: RealLikeSCU) -> H:  # type: ignore [misc]
    r"""
    Shorthand for ``#!python operator.__floordiv__(other, self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __floordiv__(other, self.h())

__rmod__(self: HableT, other: RealLikeSCU) -> H special

Shorthand for operator.__mod__(other, self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __rmod__(self: HableT, other: RealLikeSCU) -> H:
    r"""
    Shorthand for ``#!python operator.__mod__(other, self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __mod__(other, self.h())

__rmul__(self: HableT, other: RealLikeSCU) -> H special

Shorthand for operator.__mul__(other, self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __rmul__(self: HableT, other: RealLikeSCU) -> H:
    r"""
    Shorthand for ``#!python operator.__mul__(other, self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __mul__(other, self.h())

__ror__(self: HableT, other: SupportsIntSCU) -> H special

Shorthand for operator.__or__(other, self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __ror__(self: HableT, other: SupportsIntSCU) -> H:
    r"""
    Shorthand for ``#!python operator.__or__(other, self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __or__(other, self.h())

__rpow__(self: HableT, other: RealLikeSCU) -> H special

Shorthand for operator.__pow__(other, self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __rpow__(self: HableT, other: RealLikeSCU) -> H:
    r"""
    Shorthand for ``#!python operator.__pow__(other, self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __pow__(other, self.h())

__rsub__(self: HableT, other: RealLikeSCU) -> H special

Shorthand for operator.__sub__(other, self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __rsub__(self: HableT, other: RealLikeSCU) -> H:
    r"""
    Shorthand for ``#!python operator.__sub__(other, self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __sub__(other, self.h())

__rtruediv__(self: HableT, other: RealLikeSCU) -> H special

Shorthand for operator.__truediv__(other, self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __rtruediv__(self: HableT, other: RealLikeSCU) -> H:
    r"""
    Shorthand for ``#!python operator.__truediv__(other, self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __truediv__(other, self.h())

__rxor__(self: HableT, other: SupportsIntSCU) -> H special

Shorthand for operator.__xor__(other, self.h()). See the h method.

Source code in dyce/h.py
@beartype
def __rxor__(self: HableT, other: SupportsIntSCU) -> H:
    r"""
    Shorthand for ``#!python operator.__xor__(other, self.h())``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __xor__(other, self.h())

__sub__(self: HableT, other: _OperandT) -> H special

Shorthand for operator.__sub__(self.h(), other). See the h method.

Source code in dyce/h.py
@beartype
def __sub__(self: HableT, other: _OperandT) -> H:
    r"""
    Shorthand for ``#!python operator.__sub__(self.h(), other)``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __sub__(self.h(), other)

__truediv__(self: HableT, other: _OperandT) -> H special

Shorthand for operator.__truediv__(self.h(), other). See the h method.

Source code in dyce/h.py
@beartype
def __truediv__(self: HableT, other: _OperandT) -> H:
    r"""
    Shorthand for ``#!python operator.__truediv__(self.h(), other)``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __truediv__(self.h(), other)

__xor__(self: HableT, other: Union[SupportsIntSCU, H, HableT]) -> H special

Shorthand for operator.__xor__(self.h(), other). See the h method.

Source code in dyce/h.py
@beartype
def __xor__(self: HableT, other: Union[SupportsIntSCU, H, HableT]) -> H:
    r"""
    Shorthand for ``#!python operator.__xor__(self.h(), other)``. See the
    [``h`` method][dyce.h.HableT.h].
    """
    return __xor__(self.h(), other)

eq(self: HableT, other: _OperandT) -> H

Shorthand for self.h().eq(other). See the h method and H.eq.

Source code in dyce/h.py
@beartype
def eq(self: HableT, other: _OperandT) -> H:
    r"""
    Shorthand for ``#!python self.h().eq(other)``. See the
    [``h`` method][dyce.h.HableT.h] and [``H.eq``][dyce.h.H.eq].
    """
    return self.h().eq(other)

explode(self: HableT, max_depth: SupportsIntSCU = 1) -> H

Shorthand for self.h().explode(max_depth). See the h method and H.explode.

Source code in dyce/h.py
@beartype
def explode(self: HableT, max_depth: SupportsIntSCU = 1) -> H:
    r"""
    Shorthand for ``#!python self.h().explode(max_depth)``. See the
    [``h`` method][dyce.h.HableT.h] and [``H.explode``][dyce.h.H.explode].
    """
    return self.h().explode(max_depth)

ge(self: HableT, other: _OperandT) -> H

Shorthand for self.h().ge(other). See the h method and H.ge.

Source code in dyce/h.py
@beartype
def ge(self: HableT, other: _OperandT) -> H:
    r"""
    Shorthand for ``#!python self.h().ge(other)``. See the
    [``h`` method][dyce.h.HableT.h] and [``H.ge``][dyce.h.H.ge].
    """
    return self.h().ge(other)

gt(self: HableT, other: _OperandT) -> H

Shorthand for self.h().gt(other). See the h method and H.gt.

Source code in dyce/h.py
@beartype
def gt(self: HableT, other: _OperandT) -> H:
    r"""
    Shorthand for ``#!python self.h().gt(other)``. See the
    [``h`` method][dyce.h.HableT.h] and [``H.gt``][dyce.h.H.gt].
    """
    return self.h().gt(other)

is_even(self: HableT) -> H

Shorthand for self.h().is_even(). See the h method and H.is_even.

Source code in dyce/h.py
@beartype
def is_even(self: HableT) -> H:
    r"""
    Shorthand for ``#!python self.h().is_even()``. See the
    [``h`` method][dyce.h.HableT.h] and [``H.is_even``][dyce.h.H.is_even].
    """
    return self.h().is_even()

is_odd(self: HableT) -> H

Shorthand for self.h().is_odd(). See the h method and H.is_odd.

Source code in dyce/h.py
@beartype
def is_odd(self: HableT) -> H:
    r"""
    Shorthand for ``#!python self.h().is_odd()``. See the
    [``h`` method][dyce.h.HableT.h] and [``H.is_odd``][dyce.h.H.is_odd].
    """
    return self.h().is_odd()

le(self: HableT, other: _OperandT) -> H

Shorthand for self.h().le(other). See the h method and H.le.

Source code in dyce/h.py
@beartype
def le(self: HableT, other: _OperandT) -> H:
    r"""
    Shorthand for ``#!python self.h().le(other)``. See the
    [``h`` method][dyce.h.HableT.h] and [``H.le``][dyce.h.H.le].
    """
    return self.h().le(other)

lt(self: HableT, other: _OperandT) -> H

Shorthand for self.h().lt(other). See the h method and H.lt.

Source code in dyce/h.py
@beartype
def lt(self: HableT, other: _OperandT) -> H:
    r"""
    Shorthand for ``#!python self.h().lt(other)``. See the
    [``h`` method][dyce.h.HableT.h] and [``H.lt``][dyce.h.H.lt].
    """
    return self.h().lt(other)

ne(self: HableT, other: _OperandT) -> H

Shorthand for self.h().ne(other). See the h method and H.ne.

Source code in dyce/h.py
@beartype
def ne(self: HableT, other: _OperandT) -> H:
    r"""
    Shorthand for ``#!python self.h().ne(other)``. See the
    [``h`` method][dyce.h.HableT.h] and [``H.ne``][dyce.h.H.ne].
    """
    return self.h().ne(other)

substitute(self: HableT, expand: _ExpandT, coalesce: _CoalesceT = <function coalesce_replace at 0x10d423040>, max_depth: SupportsIntSCU = 1) -> H

Shorthand for self.h().substitute(expand, coalesce, max_depth). See the h method and H.substitute.

Source code in dyce/h.py
@beartype
def substitute(
    self: HableT,
    expand: _ExpandT,
    coalesce: _CoalesceT = coalesce_replace,
    max_depth: SupportsIntSCU = 1,
) -> H:
    r"""
    Shorthand for ``#!python self.h().substitute(expand, coalesce, max_depth)``. See the
    [``h`` method][dyce.h.HableT.h] and [``H.substitute``][dyce.h.H.substitute].
    """
    return self.h().substitute(expand, coalesce, max_depth)

within(self: HableT, lo: RealLikeSCU, hi: RealLikeSCU, other: _OperandT = 0) -> H

Shorthand for self.h().within(lo, hi, other). See the h method and H.within.

Source code in dyce/h.py
@beartype
def within(
    self: HableT, lo: RealLikeSCU, hi: RealLikeSCU, other: _OperandT = 0
) -> H:
    r"""
    Shorthand for ``#!python self.h().within(lo, hi, other)``. See the
    [``h`` method][dyce.h.HableT.h] and [``H.within``][dyce.h.H.within].
    """
    return self.h().within(lo, hi, other)

HableT (Protocol)

A protocol whose implementer can be expressed as (or reduced to) an H object by calling its h method. Currently, only the P class implements this protocol, but this affords an integration point for dyce users.

Info

The intended pronunciation of Hable is AYCH-uh-bul1 (i.e., H-able). Yes, that is a clumsy attempt at verbing. (You could totally H that, dude!) However, if you prefer something else (e.g. HAY-bul or AYCH-AY-bul), no one is going to judge you. (Well, they might, but they shouldn’t.) We all know what you mean.


  1. World Book Online (WBO) style pronunciation respelling

__slots__: Union[str, Iterable[str]] special

__init__(self, *args, **kwargs) special

Source code in dyce/h.py
def _no_init(self, *args, **kwargs):
    raise TypeError('Protocols cannot be instantiated')

__subclasshook__(other) special

Source code in dyce/h.py
def _proto_hook(other):
    if not cls.__dict__.get('_is_protocol', False):
        return NotImplemented

    # First, perform various sanity checks.
    if not getattr(cls, '_is_runtime_protocol', False):
        if _allow_reckless_class_cheks():
            return NotImplemented
        raise TypeError("Instance and class checks can only be used with"
                        " @runtime_checkable protocols")
    if not _is_callable_members_only(cls):
        if _allow_reckless_class_cheks():
            return NotImplemented
        raise TypeError("Protocols with non-method members"
                        " don't support issubclass()")
    if not isinstance(other, type):
        # Same error message as for issubclass(1, int).
        raise TypeError('issubclass() arg 1 must be a class')

    # Second, perform the actual structural compatibility check.
    for attr in _get_protocol_attrs(cls):
        for base in other.__mro__:
            # Check if the members appears in the class dictionary...
            if attr in base.__dict__:
                if base.__dict__[attr] is None:
                    return NotImplemented
                break

            # ...or in annotations, if it is a sub-protocol.
            annotations = getattr(base, '__annotations__', {})
            if (isinstance(annotations, collections.abc.Mapping) and
                    attr in annotations and
                    issubclass(other, Generic) and other._is_protocol):
                break
        else:
            return NotImplemented
    return True

h(self) -> H

Express its implementer as an H object.

Source code in dyce/h.py
def h(self) -> H:
    r"""
    Express its implementer as an [``H`` object][dyce.h.H].
    """
    ...

coalesce_replace(h: H, outcome: RealLikeSCU) -> H

Default behavior for H.substitute. Returns h unmodified (outcome is ignored).

Source code in dyce/h.py
def coalesce_replace(h: H, outcome: RealLikeSCU) -> H:
    r"""
    Default behavior for [``H.substitute``][dyce.h.H.substitute]. Returns *h* unmodified
    (*outcome* is ignored).
    """
    return h

resolve_dependent_probability(dependent_term: Callable[..., H], **independent_sources: _SourceT) -> H

Experimental

This method should be considered experimental and may change or disappear in future versions.

Calls dependent_term for each set of outcomes from the product of independent_sources and accumulates the results. This is useful for resolving dependent probabilities and is semantically equivalent to nesting substitution functions.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
>>> def dependent_term(
...   outcome_1,
...   outcome_2,
...   outcome_3,
...   # ...
...   outcome_n,
... ):
...   return (
...     (outcome_1 == outcome_2) +
...     (outcome_2 == outcome_3) +
...     (outcome_1 == outcome_3) +
...     (
...       outcome_n > outcome_1
...       and outcome_n > outcome_2
...       and outcome_n > outcome_3
...       # ...
...     )
...   )

>>> source_1 = H(6)
>>> source_2 = H(8)
>>> source_3 = H(10)
>>> # ...
>>> source_n = H(20)

>>> def sub_source_1(__, outcome_1):
...   def sub_source_2(__, outcome_2):
...     def sub_source_3(__, outcome_3):
...       # ...
...       def sub_source_n(__, outcome_n):
...         return dependent_term(
...           outcome_1,
...           outcome_2,
...           outcome_3,
...           # ...
...           outcome_n,
...       )
...       return source_n.substitute(sub_source_n)
...       # ...
...     return source_3.substitute(sub_source_3)
...   return source_2.substitute(sub_source_2)

>>> from dyce.h import resolve_dependent_probability
>>> h = resolve_dependent_probability(
...   dependent_term,
...   outcome_1=source_1,
...   outcome_2=source_2,
...   outcome_3=source_3,
...   # ...
...   outcome_n=source_n,
... ) ; h
H({0: 808, 1: 1700, 2: 652, 3: 7, 4: 33})
>>> source_1.substitute(sub_source_1) == h
True

This function imposes a modest overhead when compared to the nesting approach. In most cases, the improved readability is worth it.

1
2
3
4
5
%timeit resolve_dependent_probability(dependent_term, outcome_1=H(6), outcome_2=H(8), outcome_3=H(10), outcome_n=H(20))
302 ms ± 17.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit H(6).substitute(sub_source_1)
234 ms ± 7.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Source: perf_resolve_dependent_probability.ipy
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from dyce import H
from dyce.h import resolve_dependent_probability

from time import sleep
def dependent_term(
  outcome_1,
  outcome_2,
  outcome_3,
  outcome_n,
):
  import math ; math.gcd(456**123, 123**456)  # emulate an expensive calculation
  return (
    (outcome_1 == outcome_2) +
    (outcome_2 == outcome_3) +
    (outcome_1 == outcome_3) +
    (
      outcome_n > outcome_1
      and outcome_n > outcome_2
      and outcome_n > outcome_3
    )
  )

source_1 = H(6)
source_2 = H(8)
source_3 = H(10)
source_n = H(20)

def sub_source_1(__, outcome_1):
  def sub_source_2(__, outcome_2):
    def sub_source_3(__, outcome_3):
      def sub_source_n(__, outcome_n):
        return dependent_term(
          outcome_1,
          outcome_2,
          outcome_3,
          outcome_n,
        )
      return source_n.substitute(sub_source_n)
    return source_3.substitute(sub_source_3)
  return source_2.substitute(sub_source_2)

print(f"%timeit resolve_dependent_probability({dependent_term.__name__}, outcome_1={source_1}, outcome_2={source_2}, outcome_3={source_3}, outcome_n={source_n})")
%timeit resolve_dependent_probability(dependent_term, outcome_1=source_1, outcome_2=source_2, outcome_3=source_3, outcome_n=source_n)
print()

print(f"%timeit {source_1}.substitute({sub_source_1.__name__})")
%timeit source_1.substitute(sub_source_1)
print()
Source code in dyce/h.py
@beartype
def resolve_dependent_probability(
    dependent_term: Callable[..., H],
    **independent_sources: _SourceT,
) -> H:
    r"""
    !!! warning "Experimental"

        This method should be considered experimental and may change or disappear in
        future versions.

    Calls ``#!python dependent_term`` for each set of outcomes from the product of
    ``independent_sources`` and accumulates the results. This is useful for resolving
    dependent probabilities and is semantically equivalent to nesting substitution
    functions.

    ``` python
    >>> def dependent_term(
    ...   outcome_1,
    ...   outcome_2,
    ...   outcome_3,
    ...   # ...
    ...   outcome_n,
    ... ):
    ...   return (
    ...     (outcome_1 == outcome_2) +
    ...     (outcome_2 == outcome_3) +
    ...     (outcome_1 == outcome_3) +
    ...     (
    ...       outcome_n > outcome_1
    ...       and outcome_n > outcome_2
    ...       and outcome_n > outcome_3
    ...       # ...
    ...     )
    ...   )

    >>> source_1 = H(6)
    >>> source_2 = H(8)
    >>> source_3 = H(10)
    >>> # ...
    >>> source_n = H(20)

    >>> def sub_source_1(__, outcome_1):
    ...   def sub_source_2(__, outcome_2):
    ...     def sub_source_3(__, outcome_3):
    ...       # ...
    ...       def sub_source_n(__, outcome_n):
    ...         return dependent_term(
    ...           outcome_1,
    ...           outcome_2,
    ...           outcome_3,
    ...           # ...
    ...           outcome_n,
    ...       )
    ...       return source_n.substitute(sub_source_n)
    ...       # ...
    ...     return source_3.substitute(sub_source_3)
    ...   return source_2.substitute(sub_source_2)

    >>> from dyce.h import resolve_dependent_probability
    >>> h = resolve_dependent_probability(
    ...   dependent_term,
    ...   outcome_1=source_1,
    ...   outcome_2=source_2,
    ...   outcome_3=source_3,
    ...   # ...
    ...   outcome_n=source_n,
    ... ) ; h
    H({0: 808, 1: 1700, 2: 652, 3: 7, 4: 33})
    >>> source_1.substitute(sub_source_1) == h
    True

    ```

    This function imposes a modest overhead when compared to the nesting approach. In
    most cases, the improved readability is worth it.

    ``` python
    --8<-- "docs/assets/perf_resolve_dependent_probability.txt"
    ```

    <details>
    <summary>Source: <a href="https://github.com/posita/dyce/blob/latest/docs/assets/perf_resolve_dependent_probability.ipy"><code>perf_resolve_dependent_probability.ipy</code></a></summary>

    ``` python
    --8<-- "docs/assets/perf_resolve_dependent_probability.ipy"
    ```
    </details>
    """
    independent_variables_by_name: Dict[str, H] = {}

    for key, value in independent_sources.items():
        if isinstance(value, H):
            independent_variables_by_name[key] = value
        else:
            independent_variables_by_name[key] = H(value)

    captured_values_by_name: Dict[str, RealLikeSCU] = {}
    uncaptured_value_names = set(independent_variables_by_name)

    def _resolve() -> Union[H, RealLikeSCU]:
        def _capture_dependent_value(
            h: H, outcome: RealLikeSCU
        ) -> Union[RealLikeSCU, H]:
            expanded: Union[RealLikeSCU, H]
            captured_values_by_name[name_to_be_captured] = outcome
            expanded = _resolve()
            del captured_values_by_name[name_to_be_captured]

            return expanded

        if uncaptured_value_names:
            # Used in _capture_dependent_value
            name_to_be_captured = uncaptured_value_names.pop()
            expanded = independent_variables_by_name[name_to_be_captured].substitute(
                _capture_dependent_value
            )
            uncaptured_value_names.add(name_to_be_captured)
        else:
            return dependent_term(**captured_values_by_name)

        return expanded

    resolved = _resolve()

    if not isinstance(resolved, H):
        resolved = H({resolved: 1})

    return resolved

sum_h(hs: Iterable[H])

Shorthand for H({}) if h_sum == 0 else sum(hs).

This is to ensure that summing zero or more histograms always returns a histograms.

Source code in dyce/h.py
@beartype
def sum_h(hs: Iterable[H]):
    """
    Shorthand for ``#!python H({}) if h_sum == 0 else sum(hs)``.

    This is to ensure that summing zero or more histograms always returns a histograms.
    """
    h_sum = sum(hs)

    return H({}) if h_sum == 0 else h_sum