Concepts

Profile

The constructor accepts three profile forms.

  • Labeled. [[domain groups], [codomain groups]] where each group is a list of strings. The dimension of a group is its length.

  • Numeric. [[domain lengths], [codomain lengths]] where each entry is an int.

  • Tuple. (domain lengths, codomain lengths). This form is unlabeled.

profile always returns numeric lengths, [[domain], [codomain]]. labels returns (domain groups, codomain groups). An empty side, or a side that was never labeled, is None rather than [].

Axis 0 is the first domain factor. After the domain factors, axis numbers continue into the codomain. get_label_index(axis, label) returns the position of label on that axis. get_index_label(axis, index) is the inverse. Both raise ValueError when the tensor has no labels, or when label is not on that axis.

import numpy as np
from fxtensor_salmon import FXTensor

labeled = FXTensor(
    [[["Urban", "Rural"]], [["Spring", "Summer", "Other"], ["Sunny", "Rainy"]]],
    data=np.zeros((2, 3, 2)),
)
assert labeled.profile == [[2], [3, 2]]
assert labeled.labels == (
    [["Urban", "Rural"]],
    [["Spring", "Summer", "Other"], ["Sunny", "Rainy"]],
)
assert labeled.get_label_index(0, "Rural") == 1
assert labeled.get_label_index(1, "Summer") == 1
assert labeled.get_index_label(2, 0) == "Sunny"

numeric = FXTensor([[2], [3, 2]], data=np.zeros((2, 3, 2)))
assert numeric.labels == (None, None)

as_tuple = FXTensor(([2], [3]), data=np.zeros((2, 3)))
assert as_tuple.profile == [[2], [3]]
assert as_tuple.labels == (None, None)

Data

data is a NumPy array of shape tuple(domain_lengths + codomain_lengths). Omitted data is all zeros. A shape that does not match the profile raises ValueError. The empty profile [[], []] is the monoidal unit; its data is a 0-d array.

import numpy as np
from fxtensor_salmon import FXTensor

zeros = FXTensor([[2], [2]])
assert np.array_equal(zeros.data, np.zeros((2, 2)))

scalar = FXTensor([[], []], data=np.array(1.0))
assert scalar.profile == [[], []]
assert scalar.data.shape == ()
assert float(scalar.data) == 1.0

try:
    FXTensor([[2], [2]], data=np.ones((2, 3)))
except ValueError:
    pass
else:
    raise AssertionError("data shape must match the profile")

plain = FXTensor([[2], [2]], data=np.eye(2))
try:
    plain.get_label_index(0, "a")
except ValueError:
    pass
else:
    raise AssertionError("unlabeled tensors have no label lookup")

States and kernels

A state has an empty domain. A kernel has a nonempty domain. Several methods are restricted to states because the corresponding string diagram starts from the monoidal unit: conditionalization(), jointification(), and the prior argument of bayesian_inversion() and almost_surely_equal().

Equality uses numpy.allclose with FXTensor._RTOL (1e-05) and FXTensor._ATOL (1e-08), and it also compares profiles and labels. copy returns a new tensor with its own array.