Skip to content

Commit 2af6b96

Browse files
committed
feat(probability): add verified finite-table mutual information
1 parent b3c9922 commit 2af6b96

8 files changed

Lines changed: 669 additions & 5 deletions

File tree

src/jacobian/domains/probability/bundle.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from jacobian.contracts.capabilities import CapabilityDiagnostic
44
from jacobian.domain_bundles import DomainBundle
55
from jacobian.domains.probability.checkers import PROBABILITY_EXACT_REPLAY_CHECKERS
6+
from jacobian.domains.probability.mutual_information import (
7+
MUTUAL_INFORMATION_CAPABILITY,
8+
)
69
from jacobian.domains.probability.operations import FINITE_PROBABILITY_CAPABILITIES
710
from jacobian.operations import DomainDiagnostics, DomainSemantics
811
from jacobian.provider_runtime import PYTHON_FLINT_VERSION
@@ -16,12 +19,13 @@ def build_finite_probability_bundle() -> DomainBundle:
1619
schema_namespace="jacobian.validated-analysis",
1720
semantics=DomainSemantics(
1821
name="jacobian.probability",
19-
version="4",
22+
version="5",
2023
definition={
2124
"description": "bounded exact rational probability operations",
2225
"scope": (
2326
"raw moments, explicit event mass and conditioning, total "
24-
"pushforwards, independent finite convolutions, and one fixed-order "
27+
"pushforwards, independent finite convolutions, exact mutual "
28+
"information for bounded rational joint tables, one fixed-order "
2529
"moment of a sparse complex-rational polynomial in independent "
2630
"standard real Gaussian variables, and exact small-graph terminal "
2731
"connection reliability"
@@ -31,15 +35,19 @@ def build_finite_probability_bundle() -> DomainBundle:
3135
),
3236
provider_runtime=python_flint_probability_provider_runtime(),
3337
backend_version=f"python-flint-{PYTHON_FLINT_VERSION}",
34-
capabilities=FINITE_PROBABILITY_CAPABILITIES,
38+
capabilities=(
39+
MUTUAL_INFORMATION_CAPABILITY,
40+
*FINITE_PROBABILITY_CAPABILITIES,
41+
),
3542
diagnostics=DomainDiagnostics(
3643
invalid_request=CapabilityDiagnostic(
3744
code="INVALID_FINITE_PROBABILITY_REQUEST",
3845
stage="finite_probability_input_validation",
3946
message="Input does not satisfy the bounded exact-probability contract.",
4047
hint=(
41-
"Use a bounded normalized finite distribution or a bounded "
42-
"Gaussian polynomial request, or a fully weighted small graph."
48+
"Use a bounded normalized finite distribution or joint table, "
49+
"a bounded Gaussian polynomial request, or a fully weighted "
50+
"small graph."
4351
),
4452
)
4553
),

src/jacobian/domains/probability/checkers.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Independent checker declarations owned by the probability domain."""
22

33
from jacobian.checker_operations import ExactReplayCheckerDeclaration
4+
from jacobian.contracts.capabilities import CapabilityInstallTier
45
from jacobian.contracts.probability import (
56
FiniteConvolutionRequest,
67
FiniteEventRequest,
@@ -11,6 +12,8 @@
1112
from jacobian.domains.probability.gaussian_inputs import (
1213
CanonicalGaussianPolynomialMomentRequest,
1314
)
15+
from jacobian.math.probability import FiniteJointTableMutualInformationRequest
16+
from jacobian.provider_runtime import source_provider_runtime
1417

1518
_ENTRYPOINT = "jacobian_checkers.exact_probability_operations"
1619
_REASON = (
@@ -19,6 +22,43 @@
1922
)
2023

2124
PROBABILITY_EXACT_REPLAY_CHECKERS = (
25+
ExactReplayCheckerDeclaration(
26+
"probability.joint.mutual_information.compute",
27+
FiniteJointTableMutualInformationRequest,
28+
"check_finite_joint_mutual_information",
29+
"probability.finite-joint-mutual-information.fraction-replay",
30+
entrypoint_module="jacobian_checkers.mutual_information",
31+
replay_method="standard-library Fraction logarithmic-product replay",
32+
reason=(
33+
"operator-authorized standard-library Fraction checker independently "
34+
"reconstructs marginals, likelihood ratios, and the scaled log product"
35+
),
36+
provider_runtime=source_provider_runtime(
37+
"jacobian.mutual-information-checker",
38+
version="1",
39+
entrypoint=(
40+
"jacobian_checkers.mutual_information:"
41+
"check_finite_joint_mutual_information"
42+
),
43+
install_tier=CapabilityInstallTier.T1,
44+
license_id="MIT",
45+
features=("standard-library-fraction-replay", "clean-process-checker"),
46+
),
47+
verification_capability_id="probability.joint.mutual_information.verify",
48+
verification_title="Verify a finite-table mutual-information certificate",
49+
verification_description=(
50+
"Independently reconstruct ordered marginals, positive-support "
51+
"likelihood ratios, and the exact scaled logarithmic product for one "
52+
"bounded normalized rational joint table."
53+
),
54+
verification_tags=(
55+
"verification",
56+
"exact",
57+
"probability",
58+
"information-theory",
59+
"mutual-information",
60+
),
61+
),
2262
ExactReplayCheckerDeclaration(
2363
"probability.finite_distribution.raw_moment.compute",
2464
FiniteRawMomentRequest,
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"""Thin operation binding for exact finite-table mutual information."""
2+
3+
from __future__ import annotations
4+
5+
from jacobian.contracts.capabilities import CapabilityDiagnostic
6+
from jacobian.domains._examples import example
7+
from jacobian.math.probability import (
8+
FiniteJointTableMutualInformationRequest,
9+
FiniteJointTableMutualInformationResult,
10+
mutual_information,
11+
)
12+
from jacobian.operation_bindings import inline_operation
13+
from jacobian.operations import OperationRefusalError, OperationSpec
14+
15+
_INVALID_REQUEST = CapabilityDiagnostic(
16+
code="INVALID_FINITE_JOINT_TABLE_REQUEST",
17+
stage="finite_joint_table_input_validation",
18+
message="Input does not satisfy the bounded normalized rational joint-table contract.",
19+
hint=(
20+
"Supply unique ordered labels, a nonnegative rational table with at most "
21+
"64 cells summing exactly to one, and a log base from 2 to 36."
22+
),
23+
)
24+
25+
26+
def _execute(
27+
request: FiniteJointTableMutualInformationRequest,
28+
) -> FiniteJointTableMutualInformationResult:
29+
try:
30+
return mutual_information(request)
31+
except (ArithmeticError, OverflowError, ValueError) as exc:
32+
raise OperationRefusalError(
33+
CapabilityDiagnostic(
34+
code="MUTUAL_INFORMATION_CERTIFICATE_BOUND_EXCEEDED",
35+
stage="finite_joint_mutual_information",
36+
message=str(exc),
37+
hint="Reduce rational denominator sizes or the positive support.",
38+
)
39+
) from exc
40+
41+
42+
MUTUAL_INFORMATION_CAPABILITY = inline_operation(
43+
OperationSpec(
44+
operation_id="probability.joint.mutual_information.compute",
45+
version="1",
46+
title="Exact finite-table mutual information certificate",
47+
description=(
48+
"Compute ordered marginals and every positive-support likelihood "
49+
"ratio for one bounded normalized rational joint table. Return the "
50+
"exact identity scale*I=log_base(product), without floating point."
51+
),
52+
request_type=FiniteJointTableMutualInformationRequest,
53+
result_type=FiniteJointTableMutualInformationResult,
54+
execute=_execute,
55+
tags=(
56+
"probability",
57+
"information-theory",
58+
"mutual-information",
59+
"finite",
60+
"exact",
61+
"certificate",
62+
),
63+
invalid_request=_INVALID_REQUEST,
64+
invocation_examples=(
65+
example(
66+
"perfectly_correlated_fair_bits",
67+
"Compute exact base-two mutual information for two identical fair bits.",
68+
{
69+
"row_labels": ["0", "1"],
70+
"column_labels": ["0", "1"],
71+
"probabilities": [
72+
[
73+
{"num": "1", "den": "2"},
74+
{"num": "0", "den": "1"},
75+
],
76+
[
77+
{"num": "0", "den": "1"},
78+
{"num": "1", "den": "2"},
79+
],
80+
],
81+
"log_base": 2,
82+
},
83+
),
84+
),
85+
)
86+
)
87+
88+
__all__ = ["MUTUAL_INFORMATION_CAPABILITY"]

src/jacobian/math/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
matrices,
88
polynomials,
99
prime_field_linear_algebra,
10+
probability,
1011
)
1112

1213
__all__ = [
@@ -16,4 +17,5 @@
1617
"matrices",
1718
"polynomials",
1819
"prime_field_linear_algebra",
20+
"probability",
1921
]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Supported exact finite-probability APIs."""
2+
3+
from jacobian.math.probability.mutual_information import (
4+
FiniteJointLikelihoodRatio,
5+
FiniteJointTableMutualInformationRequest,
6+
FiniteJointTableMutualInformationResult,
7+
MutualInformationLogProductCertificate,
8+
mutual_information,
9+
)
10+
11+
__all__ = [
12+
"FiniteJointLikelihoodRatio",
13+
"FiniteJointTableMutualInformationRequest",
14+
"FiniteJointTableMutualInformationResult",
15+
"MutualInformationLogProductCertificate",
16+
"mutual_information",
17+
]

0 commit comments

Comments
 (0)