Probabilistic properties

is_markov() is true when every domain point’s codomain slice sums to 1 or to 0 (the zero rows produced by conditioning). A state has an empty domain, so is_markov returns False. Check a normalized state with numpy.isclose(state.data.sum(), 1).

is_deterministic() is true when every codomain slice is a point mass: one entry 1 and the rest 0. A zero slice is not deterministic. Identity, copy, swap, and discard are deterministic; a generic stochastic kernel is not.

import numpy as np
from fxtensor_salmon import FXTensor

state = FXTensor([[], [2]], data=np.array([0.2, 0.8]))
assert not state.is_markov()
assert np.isclose(state.data.sum(), 1.0)

kernel = FXTensor([[2], [2]], data=np.array([[0.2, 0.8], [0.0, 0.0]]))
assert kernel.is_markov()
assert not kernel.is_deterministic()

bad = FXTensor([[2], [2]], data=np.array([[0.2, 0.2], [0.5, 0.5]]))
assert not bad.is_markov()

identity = FXTensor.identity_tensor([2])
assert identity.is_deterministic()
assert identity.is_markov()
zeros = FXTensor([[2], [2]], data=np.zeros((2, 2)))
assert not zeros.is_deterministic()

Equality and copying

== compares profile, labels, and data. Data uses allclose with FXTensor._RTOL and FXTensor._ATOL. hash uses the raw bytes, so two tensors can compare equal and still hash differently when the stored values differ inside that tolerance. Do not use that pair as interchangeable dictionary keys.

copy (and copy.copy) duplicates the array. Later writes to one tensor do not change the other.

import numpy as np
from fxtensor_salmon import FXTensor

left = FXTensor([[2], [1]], data=np.array([[1.0], [1.0]]))
right = FXTensor([[2], [1]], data=np.array([[1.0], [1.0 + 1e-9]]))
assert left == right
assert hash(left) != hash(right)

labeled = FXTensor([[["a", "b"]], [["x", "y"]]], data=np.array([[0.2, 0.8], [0.6, 0.4]]))
relabeled = FXTensor([[["a", "b"]], [["y", "x"]]], data=np.array([[0.2, 0.8], [0.6, 0.4]]))
assert labeled != relabeled

cloned = labeled.copy()
cloned.data[0, 0] = 0.0
assert labeled.data[0, 0] == 0.2
assert cloned is not labeled