Skip to content

Commit ffdca59

Browse files
add conditional gate
1 parent d7b5012 commit ffdca59

File tree

4 files changed

+77
-9
lines changed

4 files changed

+77
-9
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010

1111
- add trigonometric methods on backends
1212

13+
- add `conditional_gate` to support quantum ops based on previous measurment results
14+
15+
### Changed
16+
17+
- change the return information of `unitary_kraus` and `general_kraus` methods
18+
1319
## 0.0.220328
1420

1521
### Added

docker/Dockerfile

+2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ RUN wget -q -P /tmp \
1010
https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh \
1111
&& bash /tmp/Miniconda3-latest-Linux-x86_64.sh -b -p /opt/conda \
1212
&& rm /tmp/Miniconda3-latest-Linux-x86_64.sh
13+
1314
ENV PATH="/opt/conda/bin:$PATH"
15+
1416
RUN conda install -y \
1517
cudatoolkit=11.0 \
1618
pip \

tensorcircuit/circuit.py

+42-9
Original file line numberDiff line numberDiff line change
@@ -787,13 +787,34 @@ def step_function(x: Tensor) -> Tensor:
787787
self.any(index, unitary=g) # type: ignore
788788
return 0.0
789789

790+
def select_gate(self, which: Tensor, kraus: Sequence[Gate], *index: int) -> None:
791+
"""
792+
Apply ``which``-th gate from ``kraus`` list, i.e. apply kraus[which]
793+
794+
:param which: Tensor of shape [] and dtype int
795+
:type which: Tensor
796+
:param kraus: A list of gate in the form of ``tc.gate`` or Tensor
797+
:type kraus: Sequence[Gate]
798+
:param index: the qubit lines the gate applied on
799+
:type index: int
800+
"""
801+
kraus = [k.tensor if isinstance(k, tn.Node) else k for k in kraus]
802+
kraus = [gates.array_to_tensor(k) for k in kraus]
803+
l = len(kraus)
804+
r = backend.onehot(which, l)
805+
r = backend.cast(r, dtype=dtypestr)
806+
tensor = reduce(add, [r[i] * kraus[i] for i in range(l)])
807+
self.any(*index, unitary=tensor) # type: ignore
808+
809+
conditional_gate = select_gate
810+
790811
def unitary_kraus2(
791812
self,
792813
kraus: Sequence[Gate],
793814
*index: int,
794815
prob: Optional[Sequence[float]] = None,
795816
status: Optional[float] = None,
796-
) -> float:
817+
) -> Tensor:
797818
# general impl from Monte Carlo trajectory depolarizing above
798819
# still jittable
799820
# speed is similar to ``unitary_kraus``
@@ -811,7 +832,19 @@ def unitary_kraus(
811832
*index: int,
812833
prob: Optional[Sequence[float]] = None,
813834
status: Optional[float] = None,
814-
) -> float:
835+
) -> Tensor:
836+
"""
837+
Apply unitary gates in ``kraus`` randomly based on corresponding ``prob``.
838+
839+
:param kraus: List of ``tc.gates.Gate`` or just Tensors
840+
:type kraus: Sequence[Gate]
841+
:param prob: prob list with the same size as ``kraus``, defaults to None
842+
:type prob: Optional[Sequence[float]], optional
843+
:param status: random seed between 0 to 1, defaults to None
844+
:type status: Optional[float], optional
845+
:return: shape [] int dtype tensor indicates which kraus gate is applied
846+
:rtype: Tensor
847+
"""
815848
# general impl from Monte Carlo trajectory depolarizing above
816849
# still jittable
817850

@@ -835,9 +868,10 @@ def _unitary_kraus_template(
835868
get_gate_from_index: Optional[
836869
Callable[[Tensor, Sequence[Tensor]], Tensor]
837870
] = None,
838-
) -> float: # DRY
871+
) -> Tensor: # DRY
839872
sites = len(index)
840873
kraus = [k.tensor if isinstance(k, tn.Node) else k for k in kraus]
874+
kraus = [gates.array_to_tensor(k) for k in kraus]
841875
if prob is None:
842876
prob = [
843877
backend.real(backend.trace(backend.adjoint(k) @ k) / k.shape[0])
@@ -867,7 +901,7 @@ def step_function(x: Tensor) -> Tensor:
867901
g = get_gate_from_index(r, kraus)
868902
g = backend.reshape(g, [2 for _ in range(sites * 2)])
869903
self.any(*index, unitary=g) # type: ignore
870-
return 0.0
904+
return r
871905

872906
def _general_kraus_tf(
873907
self,
@@ -942,7 +976,7 @@ def _general_kraus_2(
942976
kraus: Sequence[Gate],
943977
*index: int,
944978
status: Optional[float] = None,
945-
) -> float:
979+
) -> Tensor:
946980
# the graph building time is frustratingly slow, several minutes
947981
# though running time is in terms of ms
948982
# raw running time in terms of s
@@ -954,7 +988,7 @@ def _general_kraus_2(
954988
# layerwise jit technique can greatly boost the staging time, see in /examples/mcnoise_boost.py
955989
sites = len(index)
956990
kraus_tensor = [k.tensor if isinstance(k, tn.Node) else k for k in kraus]
957-
# kraus_tensor = [k.tensor for k in kraus]
991+
kraus_tensor = [gates.array_to_tensor(k) for k in kraus_tensor]
958992

959993
# tn with hole
960994
newnodes, newfront = self._copy()
@@ -988,15 +1022,14 @@ def calculate_kraus_p(i: int) -> Tensor:
9881022
for w, k in zip(prob, kraus_tensor)
9891023
]
9901024

991-
self.unitary_kraus2(new_kraus, *index, prob=prob, status=status)
992-
return 0.0
1025+
return self.unitary_kraus2(new_kraus, *index, prob=prob, status=status)
9931026

9941027
def general_kraus(
9951028
self,
9961029
kraus: Sequence[Gate],
9971030
*index: int,
9981031
status: Optional[float] = None,
999-
) -> float:
1032+
) -> Tensor:
10001033
"""
10011034
Monte Carlo trajectory simulation of general Kraus channel whose Kraus operators cannot be
10021035
amplified to unitary operators. For unitary operators composed Kraus channel, :py:meth:`unitary_kraus`

tests/test_circuit.py

+27
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,33 @@ def small_tn():
708708
np.testing.assert_allclose(small_tn(), np.zeros([2 ** n]), atol=1e-5)
709709

710710

711+
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
712+
def test_teleportation(backend):
713+
key = tc.backend.get_random_state(42)
714+
715+
@tc.backend.jit
716+
def f(key):
717+
tc.backend.set_random_state(key)
718+
c = tc.Circuit(2)
719+
c.H(0)
720+
r = c.general_kraus(
721+
[np.array([[1.0, 0], [0, 0]]), np.array([[0, 0], [0, 1]])], 0
722+
)
723+
c.conditional_gate(r, [tc.gates.i(), tc.gates.x()], 1)
724+
return r, c.expectation([tc.gates.z(), [1]])
725+
726+
keys = []
727+
for _ in range(6):
728+
key, subkey = tc.backend.random_split(key)
729+
keys.append(subkey)
730+
rs = [f(k) for k in keys]
731+
for r, e in rs:
732+
if tc.backend.numpy(r) > 0.5:
733+
np.testing.assert_allclose(e, -1, atol=1e-5)
734+
else:
735+
np.testing.assert_allclose(e, 1, atol=1e-5)
736+
737+
711738
@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")])
712739
def test_apply_mpo_gate(backend):
713740
gate = tc.gates.multicontrol_gate(tc.gates._x_matrix, ctrl=[1, 0])

0 commit comments

Comments
 (0)