条件付き独立性

is_conditionally_independent() は、状態 I → X ⊗ Y ⊗ Z または核 A → X ⊗ Y ⊗ Z が X ⊥ Y | Z を満たすかを 返します。split_x と split_y は、conditional と同じく codomain に 沿った 1 始まり の位置です。split_x より前が X、split_x から split_y の直前までが Y、残りが Z です。どのブロックも空で構いません。

判定は marginalization、conditional、copy_tensor で p(x|z) p(y|z) p(z) を組み直し、numpy.allclose で比べます。atol と rtol の既定値は FXTensor._ATOL と FXTensor._RTOL です。

is_independent() は Z が空の場合です。 split は Y の先頭因子です。

copy–discard 等式としての条件付き独立性と conditional product

図は下から上に読む(下が域、上が余域)。同時状態の紐は、図の通り X ⊗ Z ⊗ Y の順である。左は X ⊥ Y | Z の copy–discard 等式、右は conditional_product の構成 p_Z ; copy_Z ; (p(x|z) ⊗ id_Z ⊗ q(y|z))、下は Z が自明なときの 周辺の積である。API が返す余域の順は X ⊗ Y ⊗ Z``(``Z が最後)であり、 図に描かれた X ⊗ Z ⊗ Y とは並びが異なる。

条件付き積

conditional_product() は、同時状態 p_{XZ}: I → X ⊗ Z と q_{YZ}: I → Y ⊗ Z``(あるいは domain を共有する核) から、``Z を条件として条件付き独立で、それらの周辺を持つ X ⊗ Y ⊗ Z 上の 同時分布を返します。on は、両方の codomain で Z が始まる 1 始まりの 位置です。X と Y の因子数が違うときは組 (on_p, on_q) を渡します。 Z の因子サイズが違う、または Z の周辺が atol / rtol の範囲で 一致しないときは ValueError です。範囲外の split も ValueError です。

構成は p_Z ; copy_Z ; (p(x|z) ⊗ id_Z ⊗ q(y|z)) で、図の右パネル (紐は X ⊗ Z ⊗ Y)に対応します。メソッドは続いて Z を Y の後ろへ 移し、返す余域を X ⊗ Y ⊗ Z にします。

予報、天気、傘

予報が、出かける先の天気と、傘を持っていくかどうかの両方を決めます。予報を 条件にすれば、天気と傘は条件付き独立です。天気と傘だけの周辺では両者はまだ 連動するので、そこでは is_independent は偽です。

import numpy as np
from fxtensor_salmon import FXTensor

forecast = ["Sunny", "Rainy"]
weather = ["Dry", "Wet"]
umbrella = ["Leave", "Take"]
# Joints on (weather, forecast) and (umbrella, forecast). Both forecast
# marginals are [0.5, 0.5].
p_wf = FXTensor(
    [[], [weather, forecast]],
    data=np.array(
        [
            [0.45, 0.05],  # Dry
            [0.05, 0.45],  # Wet
        ]
    ),
)
p_uf = FXTensor(
    [[], [umbrella, forecast]],
    data=np.array(
        [
            [0.40, 0.10],  # Leave
            [0.10, 0.40],  # Take
        ]
    ),
)
joint = FXTensor.conditional_product(p_wf, p_uf, 2)
assert joint.labels == (None, [weather, umbrella, forecast])
assert joint.is_conditionally_independent(2, 3)
weather_umbrella = joint.marginalization(3)
assert weather_umbrella.labels == (None, [weather, umbrella])
assert not weather_umbrella.is_independent(2)

domain を共有する核

両方の引数が同じ domain を持つ核 A → · でも、同じ構成が使えます。on は、どちらの codomain でも末尾の Z 因子を指します。

import numpy as np
from fxtensor_salmon import FXTensor

context = ["Home", "Away"]
report = ["Dry", "Wet"]
carry = ["Leave", "Take"]
outlook = ["Sunny", "Rainy"]
# Each row is a distribution on report ⊗ outlook, and the outlook marginals agree.
p_report = FXTensor(
    [[context], [report, outlook]],
    data=np.array(
        [
            [[0.36, 0.04], [0.04, 0.56]],  # Home
            [[0.16, 0.16], [0.24, 0.44]],  # Away
        ]
    ),
)
p_carry = FXTensor(
    [[context], [carry, outlook]],
    data=np.array(
        [
            [[0.32, 0.12], [0.08, 0.48]],  # Home
            [[0.30, 0.18], [0.10, 0.42]],  # Away
        ]
    ),
)
kernel = FXTensor.conditional_product(p_report, p_carry, 2)
assert kernel.labels == ([context], [report, carry, outlook])
assert kernel.is_conditionally_independent(2, 3)