Skip to content

Commit

Permalink
generalize the is_causal method to all combs
Browse files Browse the repository at this point in the history
  • Loading branch information
Canoming committed Mar 15, 2024
1 parent 5909b94 commit 9401fcd
Showing 1 changed file with 46 additions and 25 deletions.
71 changes: 46 additions & 25 deletions src/qibo/quantum_info/quantum_networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,8 @@ def __init__(
self,
tensor,
partition: Optional[Union[List[int], Tuple[int]]] = None,
system_output: Optional[Union[List[bool], Tuple[bool]]] = None,
*,
system_input: Optional[Union[List[bool], Tuple[bool]]] = None,
pure: bool = False,
backend=None,
):
Expand All @@ -591,11 +592,11 @@ def __init__(
"A quantum comb should only contain equal number of input and output systems. "
+ "For general quantum networks, one should use the ``QuantumNetwork`` class.",
)
if system_output is not None:
if system_input is not None:
warning("system_output is ignored for QuantumComb")

super().__init__(
tensor, partition, [False, True] * (len(partition) // 2), pure, backend
tensor, partition, [True, False] * (len(partition) // 2), pure, backend
)

def is_causal(
Expand All @@ -622,29 +623,37 @@ def is_causal(
Returns:
bool: Causal order condition.
"""
if precision_tol < 0.0:
raise_error(
ValueError,
f"``precision_tol`` must be non-negative float, but it is {precision_tol}",
)

if order is None and self._backend.__class__.__name__ == "TensorflowBackend":
order = "euclidean"

self._tensor = self.full()
self._pure = False

partial_trace = np.einsum("jklk -> jl", self._tensor)
identity = self._backend.cast(
np.eye(partial_trace.shape[0]), dtype=partial_trace.dtype
)

norm = self._backend.calculate_norm_density_matrix(
partial_trace - identity,
order=order,
)
dim_out = self.partition[-1]
dim_in = self.partition[-2]

reduced = np.tensordot(self.full(), trace(dim_out).full(), axes=1)
print(reduced)
sub_comb = np.tensordot(reduced, trace(dim_in).full(), axes=1)
print(sub_comb)

expected = np.tensordot(sub_comb, trace(dim_in).full() / dim_in, axes=0)
print(expected)
norm = self._backend.calculate_norm((reduced - expected), order=order)
if norm > precision_tol:
return False
elif len(self.partition) == 2:
return True
else:
return QuantumComb(
sub_comb, self.partition[:-2], pure=False, backend=self._backend
).is_causal(order, precision_tol)

return float(norm) <= precision_tol
@classmethod
def from_nparray(
cls, tensor, partition=None, pure=False, backend=None, inverse=False
):
comb = super().from_nparray(tensor, partition, None, pure, backend)
if (
inverse
): # Convert mathmetical convention of Choi operator to physical convention
comb.partition = comb.partition[::-1]
comb._tensor = np.transpose(comb._tensor)
return comb


class QuantumChannel(QuantumNetwork):
Expand Down Expand Up @@ -778,6 +787,7 @@ def link_product(
*operands: QuantumNetwork,
backend=None,
surpress_warning=False,
casting: bool = True,
):
"""Link product between two quantum networks.
Expand Down Expand Up @@ -866,6 +876,17 @@ def link_product(

new_tensor = np.einsum(subscripts, *tensors)

if casting:
try:
network = QuantumChannel(new_tensor, partition, backend=backend)
return network
except:
try:
network = QuantumComb(new_tensor, partition, backend=backend)
return network
except:
pass

return QuantumNetwork(new_tensor, partition, system_input, backend=backend)


Expand Down

0 comments on commit 9401fcd

Please sign in to comment.