FXTensor¶
A tensor with support for string-labeled dimensions and tensor operations. |
|
Get the tensor's profile as a list of lists [[domain], [codomain]]. |
|
Get the tensor's string labels. |
|
Axis names |
|
Return a copy with axis names replaced. |
|
Return the axis index of |
|
Get the index of a label for a given axis. |
|
Get the label for a given axis and index. |
|
Standard tensor composition. |
|
Perform tensor product operation. |
|
Marginalize out a part of the codomain by summing over it. |
|
Keep the named codomain axes, in the order given, and sum out the rest. |
|
Sum out the named codomain axes in |
|
Reorder the codomain axes into |
|
Condition a state on named outcomes. |
|
Discard a prefix of the codomain by summing over it. |
|
Marginalize out the second part of the codomain. |
|
Marginalize out the first part of the codomain. |
|
Perform partial composition. |
|
Create a joint state from two tensors. |
|
Create a conditional probability distribution from a joint state. |
|
Conditional of a kernel |
|
Return whether |
|
Return whether |
|
Return the conditional product, with codomain |
|
Return whether this state is invariant under every adjacent swap. |
|
Return the average of this state over every permutation of |
|
Return whether discarding the last factor reproduces the previous state. |
|
Return the tensor product of |
|
Return |
|
Bayesian inversion of |
|
Indicator tensor of the nonzero entries (same profile and labels). |
|
Check |
|
Check |
|
Check if the tensor is a Markov tensor. |
|
Check whether every codomain slice is a point mass (one-hot). |
|
Create a tensor from JSON data. |
|
Create a tensor from strands (string representation). |
|
Convert the tensor to a JSON-serializable dictionary. |
|
Save the tensor to a JSON file. |
|
Load a tensor from a JSON file. |
|
Convert tensor to original dictionary format with 1-based indexing. |
|
Create an identity tensor. |
|
Create a diagonal copy tensor |
|
Create a delta tensor (identity matrix) with the given dimensions. |
|
Create a swap tensor. |
|
Create an exclamation tensor (discarding). |
|
Create a unit tensor (all ones) with the given dimensions. |
- class fxtensor_salmon.FXTensor(profile, data=None, names=None)[source]¶
A tensor with support for string-labeled dimensions and tensor operations.
Optional
namesgive each axis a variable name. Names are not the labels of the values on an axis.names=Noneleaves the tensor unnamed.- Parameters:
profile (Union[List[List[Union[int, List[str]]]], Tuple[List[int], List[int]]])
data (Optional[np.ndarray])
names (Optional[Tuple[Optional[List[str]], Optional[List[str]]]])
- property profile: List[List[int]]¶
Get the tensor’s profile as a list of lists [[domain], [codomain]].
- property labels: Tuple[List[List[str]], List[List[str]]] | None¶
Get the tensor’s string labels.
- property names: Tuple[List[str] | None, List[str] | None]¶
Axis names
(domain, codomain).(None, None)means the tensor was constructed without names.Noneon one side means that side has no axes. Names are unique across the tensor and are not value labels.
- get_label_index(axis, label)[source]¶
Get the index of a label for a given axis.
- Parameters:
axis (int)
label (str)
- Return type:
int
- get_index_label(axis, index)[source]¶
Get the label for a given axis and index.
- Parameters:
axis (int)
index (int)
- Return type:
str
- almost_surely_equal(other, prior)¶
Check
self = otherprior-almost surely.For
f, g: X → Yand a statep: I → Xthis is Fritz’sp ; copy ; (id ⊗ f) = p ; copy ; (id ⊗ g); for finite tensors it meansf(·|x) = g(·|x)for everyxwithp(x) > 0. Only profiles are compared, not labels.
- axis(name)¶
Return the axis index of
name.Domain axes are counted first, then codomain axes, as in
get_label_index().- Parameters:
name (str)
- Return type:
int
- bayesian_inversion(prior)¶
Bayesian inversion of
f: X → Ywith respect toprior: I → X.Returns
f†: Y → Xwithf†(x|y) = prior(x) f(y|x) / Σ_x' prior(x') f(y|x')(Cho & Jacobs 2017). Rows with zero evidence stay zero. Labels are swapped fromself(or taken frompriorforX).
- conditional(concat_start_index)¶
Conditional of a kernel
f: A → X ⊗ YasA ⊗ X → Y.Generalizes
conditionalization()to tensors with a domain.concat_start_index(1-based) is the first codomain axis ofY. Entries withf_X(x|a) = 0stay zero, as inconditionalization(). For a state the result equalsconditionalization(concat_start_index).- Parameters:
concat_start_index (int)
- Return type:
- classmethod conditional_product(p_xz, q_yz, on, atol=None, rtol=None)¶
Return the conditional product, with codomain
X ⊗ Y ⊗ Z.p_xzhas codomainX ⊗ Zandq_yzhas codomainY ⊗ Z.onis the 1-based index of the firstZfactor in both codomains, or a pair(on_p, on_q)whenXandYhave different numbers of factors. Both arguments are states, or both are kernels with the same domain.The value is
p_Z ; copy_Z ; (p(x|z) ⊗ id_Z ⊗ q(y|z))withZthen moved pastY. That diagram has wiresX ⊗ Z ⊗ Y; the tensor returned here isX ⊗ Y ⊗ Z, which satisfiesX ⊥ Y | Z.p_Zis theZmarginal ofp_xz. The twoZmarginals must agree withinatolandrtol(defaults:FXTensor._ATOLandFXTensor._RTOL).- Raises:
ValueError – If
onis out of range, the domains differ, theZfactor sizes differ, or theZmarginals disagree.
- conditionalization(concat_start_index)¶
Create a conditional probability distribution from a joint state.
- Parameters:
concat_start_index (int)
- Return type:
- classmethod copy_tensor(list_x, n=2)¶
Create a diagonal copy tensor
X → X^{⊗ n}.n=0is discard (exclamation),n=1is identity.- Parameters:
list_x (Union[List[int], List[List[str]]])
n (int)
- Return type:
- classmethod delta_tensor(dims)¶
Create a delta tensor (identity matrix) with the given dimensions.
- Parameters:
dims (List[int])
- Return type:
- discard_prefix(start_B)¶
Discard a prefix of the codomain by summing over it.
- Parameters:
start_B (int)
- Return type:
- classmethod exclamation(list_x)¶
Create an exclamation tensor (discarding).
- Parameters:
list_x (List[int])
- Return type:
- first_marginalization(concat_start_index)¶
Marginalize out the second part of the codomain.
- Parameters:
concat_start_index (int)
- Return type:
- classmethod from_json(json_data)¶
Create a tensor from JSON data.
A
namesentry, when present, is[domain names, codomain names]. JSON written before axis names existed has nonameskey; those tensors stay unnamed.- Parameters:
json_data (Dict[str, Any])
- Return type:
- classmethod from_strands(profile, strands)¶
Create a tensor from strands (string representation).
- Parameters:
profile (Union[Tuple[List[int], List[int]], List[List[List[str]]]])
strands (Dict[str, Union[float, Fraction]])
- Return type:
- classmethod identity_tensor(list_x)¶
Create an identity tensor.
- Parameters:
list_x (Union[List[int], List[List[str]]])
- Return type:
- classmethod iid(p, n)¶
Return the tensor product of
ncopies ofp.Labels are taken from
p. For a stateI → Xthe result isI → X^{⊗n}.n=1returns a copy ofp.- Raises:
ValueError – If
pis not anFXTensor, ornis not a positive integer.
- is_absolutely_continuous(other)¶
Check
self ≪ other: whereverotheris zero,selfis zero.For states this is the usual absolute continuity of distributions; for kernels
f, g: A → Xit holds input-wise, i.e.f(·|a) ≪ g(·|a)for everya(finite case of Fritz et al., arXiv:2308.00651). Only profiles are compared, not labels.- Parameters:
other (FXTensor)
- Return type:
bool
- is_conditionally_independent(split_x, split_y, atol=None, rtol=None)¶
Return whether
X ⊥ Y | Zfor codomain orderX ⊗ Y ⊗ Z.split_xandsplit_yare 1-based, likeconditional. Factors1 .. split_x - 1areX, factorssplit_x .. split_y - 1areY, and the remaining factors areZ. Any of the three blocks may be empty. A stateI → X ⊗ Y ⊗ Zand a kernelA → X ⊗ Y ⊗ Zuse the same splits.The predicate holds when
selfequals the conditional product of itsX ⊗ ZandY ⊗ Zmarginals: the finite form of Fritz’s copy–discard equation (arXiv:1908.07021, Def. 12.1 for states and Def. 12.16 for kernels). Values are compared withnumpy.allclose.atolandrtoldefault toFXTensor._ATOLandFXTensor._RTOL.- Raises:
ValueError – If a split is not a 1-based index in range.
- is_deterministic()¶
Check whether every codomain slice is a point mass (one-hot).
In finite stochastic matrices this is equivalent to Fritz’s definition
f ; copy = copy ; (f ⊗ f)for Markov kernels. Zero slices are not considered deterministic.- Return type:
bool
- is_exchangeable(block=None, atol=None, rtol=None)¶
Return whether this state is invariant under every adjacent swap.
The state
p: I → X^{⊗n}is exchangeable at positionk(1-based) whenp ; (id^{⊗(k-1)} ⊗ swap(X, X) ⊗ id^{⊗(n-k-1)}) = p.Swapping the
k-th and(k+1)-th wires leavespunchanged. The identity wires and the two swapped wires use(k-1) + 2 + (n-k-1) = nfactors. Adjacent transpositions generate the symmetric group, so this checksk = 1 .. n-1.n < 2is vacuously true.blockis the dimension list of one factorX. The default,None, takes each codomain axis as one factor; those axes must share a size.swapcarries no labels, so the comparison is ondatavianumpy.allclose.atolandrtoldefault toFXTensor._ATOLandFXTensor._RTOL.- Raises:
ValueError – If
selfis not a state, or the codomain is notX^{⊗n}.
- is_independent(split, atol=None, rtol=None)¶
Return whether
X ⊥ Ywhen the codomain isX ⊗ Y.splitis the 1-based first factor ofY. This isis_conditionally_independent()with an emptyZ.atolandrtolare forwarded.- Raises:
ValueError – If
splitis not a 1-based index in range.
- classmethod is_kolmogorov_consistent(states, block=None, atol=None, rtol=None)¶
Return whether discarding the last factor reproduces the previous state.
statesis the list[p_1, p_2, ..., p_N]withp_n: I → X^{⊗n}(sostates[0]isp_1). The sequence is Kolmogorov consistent when, for everyn ≥ 2,p_n ; (id^{⊗(n-1)} ⊗ !) = p_{n-1}.!isexclamation()on the last factor: discarding then-th wire ofp_nyieldsp_{n-1}. A list of length 0 or 1 is consistent.blockselectsXas inis_exchangeable(); the default is one axis, andstates[n-1]must then havenaxes of that size. Values are compared ondatawithnumpy.allclose.atolandrtoldefault toFXTensor._ATOLandFXTensor._RTOL.- Raises:
ValueError – If an entry is not a state
I → X^{⊗n}for that position, or the factors are not a common typeX.
- is_markov()¶
Check if the tensor is a Markov tensor.
For every domain index the codomain slice must sum to 1 (or 0, so that zero rows produced by
conditionalization()are accepted). States (empty domain) always returnFalse; checknp.isclose(state.data.sum(), 1)for a normalized state.- Return type:
bool
- jointification(other)¶
Create a joint state from two tensors.
- classmethod load_from_file(filename)¶
Load a tensor from a JSON file.
- Parameters:
filename (str)
- Return type:
- marginal(*keep)¶
Keep the named codomain axes, in the order given, and sum out the rest.
A middle axis can be kept or dropped. For a state
p: I → A ⊗ B ⊗ C,p.marginal("B")equalsp ; (! ⊗ id_B ⊗ !): the deterministic wiring that discards every codomain wire exceptB. Labels move with the kept axes. The domain is unchanged.- Parameters:
keep (str)
- Return type:
- marginalization(start_B)¶
Marginalize out a part of the codomain by summing over it.
- Parameters:
start_B (int)
- Return type:
- marginalize(drop)¶
Sum out the named codomain axes in
drop.The remaining codomain axes stay in their original order. This is
marginalof the complement ofdrop.- Parameters:
drop (Sequence[str])
- Return type:
- classmethod mixture_of_iid(prior, kernel, n)¶
Return
prior ; copy_Θ^{(n)} ; kernel^{⊗n}.prioris a stateI → Θandkernelis a kernelΘ → X. The result is a stateI → X^{⊗n}: one draw from the prior, copied, thennindependent samples from the kernel. It is exchangeable and, asnvaries, Kolmogorov consistent. Codomain labels are the codomain labels ofkernel, repeatedntimes.- Raises:
ValueError – If
prioris not a state, the profiles do not meet, ornis not a positive integer.
- observe(**evidence)¶
Condition a state on named outcomes.
evidencemaps an axis name to a label, or to an integer index when that axis has no labels. The result is(posterior, probability): the posterior is the joint state on the remaining axes, in their original order, and the probability is the evidence. Observing several axes at once matches observing them one at a time; the probability is then the joint probability of all of the outcomes.The posterior is the row of
conditionalization()afterpermute()moves the observed wires to the front. Evidence 0 raisesValueError.- Raises:
ValueError – If
selfis not a state, a name or label is unknown, or the evidence has probability 0.- Return type:
Tuple[‘FXTensor’, float]
- partial_composition(other, concat_start_index)¶
Perform partial composition.
- permute(order)¶
Reorder the codomain axes into
order.The domain is unchanged. Labels move with their axes.
ordermust be a permutation of the codomain names. For a state the codomain is the whole profile, so this reorders every axis.- Parameters:
order (Sequence[str])
- Return type:
- rename(**mapping)¶
Return a copy with axis names replaced.
Each keyword is an existing name and its value is the new name. Names that are not mentioned stay. The result must still be unique.
- Parameters:
mapping (str)
- Return type:
- save_to_file(filename)¶
Save the tensor to a JSON file.
- Parameters:
filename (str)
- Return type:
None
- second_marginalization(concat_start_index)¶
Marginalize out the first part of the codomain.
- Parameters:
concat_start_index (int)
- Return type:
- support()¶
Indicator tensor of the nonzero entries (same profile and labels).
For a state
p: I → Xthis is the support{x : p(x) > 0}; for a kernelf: A → Xit is the support relation{(a, x) : f(x|a) > 0}. Entries within_ATOLof zero count as zero.- Return type:
- classmethod swap(list_a, list_b)¶
Create a swap tensor.
- Parameters:
list_a (List[int])
list_b (List[int])
- Return type:
- symmetrize(block=None)¶
Return the average of this state over every permutation of
X.The result has the same profile and labels and satisfies
is_exchangeable()for the sameblock. Averaging again changes nothing, and an exchangeable state is left unchanged.n > 8warns: the average runs throughn!permutations.- Raises:
ValueError – If
selfis not a state, or the codomain is notX^{⊗n}.
- tensor_product(other)¶
Perform tensor product operation.
- to_json()¶
Convert the tensor to a JSON-serializable dictionary.
Unnamed tensors omit
names. Named tensors store[domain names, codomain names]undernames.- Return type:
dict
- to_original_dict()¶
Convert tensor to original dictionary format with 1-based indexing.
- Return type:
Dict[str, Any]
The conditional-independence methods are implemented in
fxtensor_salmon.independence and mixed into FXTensor.
Conditional independence for finite Markov kernels.
Fritz’s copy–discard equation (arXiv:1908.07021, Definitions 12.1 and 12.16)
and the conditional product (Definition 12.8). Codomains are ordered
X ⊗ Y ⊗ Z. The string diagram draws the same variables as X ⊗ Z ⊗ Y.
- class fxtensor_salmon.independence.IndependenceMixin[source]
Bases:
objectMixin that adds conditional independence to
FXTensor.Codomains are ordered
X ⊗ Y ⊗ Z. The string diagram draws the same variables asX ⊗ Z ⊗ Y.- is_conditionally_independent(split_x, split_y, atol=None, rtol=None)[source]
Return whether
X ⊥ Y | Zfor codomain orderX ⊗ Y ⊗ Z.split_xandsplit_yare 1-based, likeconditional. Factors1 .. split_x - 1areX, factorssplit_x .. split_y - 1areY, and the remaining factors areZ. Any of the three blocks may be empty. A stateI → X ⊗ Y ⊗ Zand a kernelA → X ⊗ Y ⊗ Zuse the same splits.The predicate holds when
selfequals the conditional product of itsX ⊗ ZandY ⊗ Zmarginals: the finite form of Fritz’s copy–discard equation (arXiv:1908.07021, Def. 12.1 for states and Def. 12.16 for kernels). Values are compared withnumpy.allclose.atolandrtoldefault toFXTensor._ATOLandFXTensor._RTOL.- Raises:
ValueError – If a split is not a 1-based index in range.
- is_independent(split, atol=None, rtol=None)[source]
Return whether
X ⊥ Ywhen the codomain isX ⊗ Y.splitis the 1-based first factor ofY. This isis_conditionally_independent()with an emptyZ.atolandrtolare forwarded.- Raises:
ValueError – If
splitis not a 1-based index in range.
- classmethod conditional_product(p_xz, q_yz, on, atol=None, rtol=None)[source]
Return the conditional product, with codomain
X ⊗ Y ⊗ Z.p_xzhas codomainX ⊗ Zandq_yzhas codomainY ⊗ Z.onis the 1-based index of the firstZfactor in both codomains, or a pair(on_p, on_q)whenXandYhave different numbers of factors. Both arguments are states, or both are kernels with the same domain.The value is
p_Z ; copy_Z ; (p(x|z) ⊗ id_Z ⊗ q(y|z))withZthen moved pastY. That diagram has wiresX ⊗ Z ⊗ Y; the tensor returned here isX ⊗ Y ⊗ Z, which satisfiesX ⊥ Y | Z.p_Zis theZmarginal ofp_xz. The twoZmarginals must agree withinatolandrtol(defaults:FXTensor._ATOLandFXTensor._RTOL).- Raises:
ValueError – If
onis out of range, the domains differ, theZfactor sizes differ, or theZmarginals disagree.
The exchangeability methods are implemented in
fxtensor_salmon.exchangeability and mixed into FXTensor.
Exchangeability and Kolmogorov consistency for finite states.
A state p: I → X^{⊗n} is exchangeable at position k (1-based) when
p ; (id^{⊗(k-1)} ⊗ swap(X, X) ⊗ id^{⊗(n-k-1)}) = p.
Swapping the k-th and (k+1)-th wires leaves p unchanged.
Those wires use (k-1) + 2 + (n-k-1) = n factors.
A sequence p_n: I → X^{⊗n} is Kolmogorov consistent when
p_n ; (id^{⊗(n-1)} ⊗ !) = p_{n-1}.
Discarding the n-th wire of p_n yields p_{n-1}.
- class fxtensor_salmon.exchangeability.ExchangeabilityMixin[source]
Bases:
objectMixin that adds exchangeability and Kolmogorov consistency.
States are morphisms
I → X^{⊗n}.Xis one codomain axis unlessblockgives the dimension list of a multi-axis factor.- is_exchangeable(block=None, atol=None, rtol=None)[source]
Return whether this state is invariant under every adjacent swap.
The state
p: I → X^{⊗n}is exchangeable at positionk(1-based) whenp ; (id^{⊗(k-1)} ⊗ swap(X, X) ⊗ id^{⊗(n-k-1)}) = p.Swapping the
k-th and(k+1)-th wires leavespunchanged. The identity wires and the two swapped wires use(k-1) + 2 + (n-k-1) = nfactors. Adjacent transpositions generate the symmetric group, so this checksk = 1 .. n-1.n < 2is vacuously true.blockis the dimension list of one factorX. The default,None, takes each codomain axis as one factor; those axes must share a size.swapcarries no labels, so the comparison is ondatavianumpy.allclose.atolandrtoldefault toFXTensor._ATOLandFXTensor._RTOL.- Raises:
ValueError – If
selfis not a state, or the codomain is notX^{⊗n}.
- symmetrize(block=None)[source]
Return the average of this state over every permutation of
X.The result has the same profile and labels and satisfies
is_exchangeable()for the sameblock. Averaging again changes nothing, and an exchangeable state is left unchanged.n > 8warns: the average runs throughn!permutations.- Raises:
ValueError – If
selfis not a state, or the codomain is notX^{⊗n}.
- classmethod is_kolmogorov_consistent(states, block=None, atol=None, rtol=None)[source]
Return whether discarding the last factor reproduces the previous state.
statesis the list[p_1, p_2, ..., p_N]withp_n: I → X^{⊗n}(sostates[0]isp_1). The sequence is Kolmogorov consistent when, for everyn ≥ 2,p_n ; (id^{⊗(n-1)} ⊗ !) = p_{n-1}.!isexclamation()on the last factor: discarding then-th wire ofp_nyieldsp_{n-1}. A list of length 0 or 1 is consistent.blockselectsXas inis_exchangeable(); the default is one axis, andstates[n-1]must then havenaxes of that size. Values are compared ondatawithnumpy.allclose.atolandrtoldefault toFXTensor._ATOLandFXTensor._RTOL.- Raises:
ValueError – If an entry is not a state
I → X^{⊗n}for that position, or the factors are not a common typeX.
- classmethod iid(p, n)[source]
Return the tensor product of
ncopies ofp.Labels are taken from
p. For a stateI → Xthe result isI → X^{⊗n}.n=1returns a copy ofp.- Raises:
ValueError – If
pis not anFXTensor, ornis not a positive integer.
- classmethod mixture_of_iid(prior, kernel, n)[source]
Return
prior ; copy_Θ^{(n)} ; kernel^{⊗n}.prioris a stateI → Θandkernelis a kernelΘ → X. The result is a stateI → X^{⊗n}: one draw from the prior, copied, thennindependent samples from the kernel. It is exchangeable and, asnvaries, Kolmogorov consistent. Codomain labels are the codomain labels ofkernel, repeatedntimes.- Raises:
ValueError – If
prioris not a state, the profiles do not meet, ornis not a positive integer.
The named-axis methods are implemented in fxtensor_salmon.naming
and mixed into FXTensor.
Axis names for FXTensor: permute, marginal, and observe.
Names are variable names on wires. They are not the labels of the points
on a wire. A tensor constructed with names=None stays unnamed, and the
numeric operations are unchanged.
- class fxtensor_salmon.naming.NamingMixin[source]
Bases:
objectName-based permute, marginal, and observe.
- rename(**mapping)[source]
Return a copy with axis names replaced.
Each keyword is an existing name and its value is the new name. Names that are not mentioned stay. The result must still be unique.
- Parameters:
mapping (str)
- Return type:
- axis(name)[source]
Return the axis index of
name.Domain axes are counted first, then codomain axes, as in
get_label_index().- Parameters:
name (str)
- Return type:
int
- permute(order)[source]
Reorder the codomain axes into
order.The domain is unchanged. Labels move with their axes.
ordermust be a permutation of the codomain names. For a state the codomain is the whole profile, so this reorders every axis.- Parameters:
order (Sequence[str])
- Return type:
- marginal(*keep)[source]
Keep the named codomain axes, in the order given, and sum out the rest.
A middle axis can be kept or dropped. For a state
p: I → A ⊗ B ⊗ C,p.marginal("B")equalsp ; (! ⊗ id_B ⊗ !): the deterministic wiring that discards every codomain wire exceptB. Labels move with the kept axes. The domain is unchanged.- Parameters:
keep (str)
- Return type:
- marginalize(drop)[source]
Sum out the named codomain axes in
drop.The remaining codomain axes stay in their original order. This is
marginalof the complement ofdrop.- Parameters:
drop (Sequence[str])
- Return type:
- observe(**evidence)[source]
Condition a state on named outcomes.
evidencemaps an axis name to a label, or to an integer index when that axis has no labels. The result is(posterior, probability): the posterior is the joint state on the remaining axes, in their original order, and the probability is the evidence. Observing several axes at once matches observing them one at a time; the probability is then the joint probability of all of the outcomes.The posterior is the row of
conditionalization()afterpermute()moves the observed wires to the front. Evidence 0 raisesValueError.- Raises:
ValueError – If
selfis not a state, a name or label is unknown, or the evidence has probability 0.- Return type:
Tuple[‘FXTensor’, float]