IBPSolution

Symbolica documentation for getting started, symbolic expressions, numerical evaluation, pattern matching, and APIs in Python and Rust.

IBPSolution

class IBPSolution

Rules, unresolved integrals and statistics returned by an IBP search.

Obtain a solution from IBPFamily.solve_parametric or reduce_laporta; there is no public constructor. reduce chooses the first applicable rule. For parametric solutions it takes one recurrence step. Laporta solutions already back-substitute solved targets. Unmatched integrals remain explicit.

Residual integrals are the unresolved basis at the chosen search depth; they are not certified independent masters. Rule conditions still apply when masses, invariants or the dimension are specialized.

Examples

from symbolica import S, E
from symbolica.community import hepkit as hep
d, k, m2 = S("d", "k", "m2")
kin = hep.Kinematics(d, momenta=[k])
family = hep.IntegralFamily([k], [], [kin.scalar_product(k, k) - m2], kinematics=kin)
ibp = hep.IBPFamily(family, name="T")
solution = ibp.reduce_laporta([[3]], max_depth=1)
I = S("I")
reduced = solution.reduce([3], integral=I)
expected = (d-2)*(d-4)/(8*m2**2)*I(1)
assert (reduced - expected).together() == E("0")
assert [1] in solution.residuals

Attributes

Name Description
depth Seed depth of a Laporta search; None for parametric rules.
preferred_masters Requested powers and their replaced/residual status, in request order.
replaced Search residuals replaced by the preferred masters.
residuals Integer power vectors left unresolved by the finite-target search
rules Solved identities in application order
stable_depth First depth reproduced by the next two depths; a stability heuristic.
stats Search counters: sectors, seeds, rows, and exact_trace_rows

depth

IBPSolution.depth: int | None

Seed depth of a Laporta search; None for parametric rules.

Examples

Using the setup in the IBPSolution class example:

assert solution.depth == 1

preferred_masters

IBPSolution.preferred_masters: list[tuple[list[int], str]]

Requested powers and their replaced/residual status, in request order.

Examples

Using the setup in the IBPSolution class example:

assert solution.preferred_masters == []  # no preferred basis requested

replaced

IBPSolution.replaced: list[list[int]]

Search residuals replaced by the preferred masters.

Examples

Using the setup in the IBPSolution class example:

assert solution.replaced == []  # the solver's residual basis is retained

residuals

IBPSolution.residuals: list[list[int]]

Integer power vectors left unresolved by the finite-target search.

The list describes this search’s remaining basis, not a proof of master independence. Parametric searches may have no residual list even though a recurrence leaves boundary cases unsolved.

Examples

Using the setup in the IBPSolution class example:

assert [1] in solution.residuals
I = S("I")
remaining_integrals = [I(*powers) for powers in solution.residuals]

rules

IBPSolution.rules: list[IBPRule]

Solved identities in application order.

Inspect each rule’s target, sector, nonzero conditions and exceptions before using it at specialized kinematics. An empty list means no solved rules were found.

Examples

Using the setup in the IBPSolution class example:

assert solution.rules
for rule in solution.rules:
    identity = (rule.target, rule.terms)

stable_depth

IBPSolution.stable_depth: int | None

First depth reproduced by the next two depths; a stability heuristic.

Examples

Using the setup in the IBPSolution class example:

assert solution.stable_depth is None  # no stability search requested

stats

IBPSolution.stats: dict[str, int]

Search counters: sectors, seeds, rows, and exact_trace_rows.

Use these counts to compare search effort at different depths. They report work performed, not completeness or the number of independent masters.

Examples

Using the setup in the IBPSolution class example:

assert solution.stats["rows"] > 0
seed_count = solution.stats["seeds"]

Methods

Name Description
__repr__ Summarize this IBPSolution for interactive inspection.
certify Replay exact Laporta identities and optionally check generic sector master counts
reduce Apply the first valid rule, leaving unmatched integrals unchanged

__repr__

IBPSolution.__repr__() -> str

Summarize this IBPSolution for interactive inspection.

Examples

Using the setup in the IBPSolution class example:

summary = repr(solution)

certify

IBPSolution.certify(
    *,
    count_masters: bool = True,
    replay: bool = True,
    seed: int = 0,
) -> IBPCertificate

Replay exact Laporta identities and optionally check generic sector master counts.

Parametric solutions cannot be certified. A count-consistent result is not a proof that all residual integrals are independent.

Examples

Using the setup in the IBPSolution class example:

certificate = solution.certify(count_masters=False)
assert certificate.reduction == "verified"
assert certificate.masters == "unchecked"

Parameters

  • count_masters (bool, optional) Request probabilistic generic sector master counts; default True.
  • replay (bool, optional) Reconstruct the reduction from original exact identities; default True.
  • seed (int, optional) Nonnegative seed for probabilistic master counting; default 0.

reduce

IBPSolution.reduce(
    powers: list[int],
    *,
    integral: None = None,
) -> list[tuple[list[int], Expression]]
IBPSolution.reduce(powers: list[int], *, integral: Expression) -> Expression

Apply the first valid rule, leaving unmatched integrals unchanged.

Returns (powers, coefficient) terms by default. With a bare Symbolica symbol as integral, returns their symbolic sum; an empty sum is zero. Laporta rules are back-substituted, whereas parametric rules perform one recurrence step. A rule’s symbolic kinematic conditions still apply.

Wrong power-vector length or a non-symbol integral raises ValueError. Unmatched input returns [(powers, 1)] or integral(*powers) rather than an error; inspect the returned powers to see what remains.

Examples

Using the setup in the IBPSolution class example:

I = S("I")
terms = solution.reduce([3])
assert terms[0][0] == [1]
assert solution.reduce([1], integral=I) == I(1)

Parameters

  • powers (list[int]) One signed 64-bit integer per denominator; booleans are rejected.
  • integral (Expression or None, optional) Bare function-head symbol, e.g. S(“I”); None returns structured terms.