Skip to content

Commit d4507fb

Browse files
Merge branch 'noisemodels' of https://github.com/yutuer21/tensorcircuit into tqlpr77
2 parents 22faa8b + 8ef223e commit d4507fb

8 files changed

+399
-118
lines changed

tensorcircuit/abstractcircuit.py

+30-1
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,9 @@ def expectation(
866866
self,
867867
*ops: Tuple[tn.Node, List[int]],
868868
reuse: bool = True,
869+
noise_conf: Optional[Any] = None,
870+
nmc: int = 1000,
871+
status: Optional[Tensor] = None,
869872
**kws: Any,
870873
) -> Tensor:
871874
raise NotImplementedError
@@ -876,6 +879,9 @@ def expectation_ps(
876879
y: Optional[Sequence[int]] = None,
877880
z: Optional[Sequence[int]] = None,
878881
reuse: bool = True,
882+
noise_conf: Optional[Any] = None,
883+
nmc: int = 1000,
884+
status: Optional[Tensor] = None,
879885
**kws: Any,
880886
) -> Tensor:
881887
"""
@@ -890,6 +896,20 @@ def expectation_ps(
890896
>>> c.expectation_ps(x=[1], z=[0])
891897
array(-0.99999994+0.j, dtype=complex64)
892898
899+
>>> c = tc.Circuit(2)
900+
>>> c.cnot(0, 1)
901+
>>> c.rx(0, theta=0.4)
902+
>>> c.rx(1, theta=0.8)
903+
>>> c.h(0)
904+
>>> c.h(1)
905+
>>> error1 = tc.channels.generaldepolarizingchannel(0.1, 1)
906+
>>> error2 = tc.channels.generaldepolarizingchannel(0.06, 2)
907+
>>> noise_conf = NoiseConf()
908+
>>> noise_conf.add_noise("rx", error1)
909+
>>> noise_conf.add_noise("cnot", [error2], [[0, 1]])
910+
>>> c.expectation_ps(x=[0], noise_conf=noise_conf, nmc=10000)
911+
(0.46274087-3.764033e-09j)
912+
893913
:param x: sites to apply X gate, defaults to None
894914
:type x: Optional[Sequence[int]], optional
895915
:param y: sites to apply Y gate, defaults to None
@@ -898,6 +918,13 @@ def expectation_ps(
898918
:type z: Optional[Sequence[int]], optional
899919
:param reuse: whether to cache and reuse the wavefunction, defaults to True
900920
:type reuse: bool, optional
921+
:param noise_conf: Noise Configuration, defaults to None
922+
:type noise_conf: Optional[NoiseConf], optional
923+
:param nmc: repetition time for Monte Carlo sampling for noisfy calculation, defaults to 1000
924+
:type nmc: int, optional
925+
:param status: external randomness given by tensor uniformly from [0, 1], defaults to None,
926+
used for noisfy circuit sampling
927+
:type status: Optional[Tensor], optional
901928
:return: Expectation value
902929
:rtype: Tensor
903930
"""
@@ -911,4 +938,6 @@ def expectation_ps(
911938
if z is not None:
912939
for i in z:
913940
obs.append([gates.z(), [i]]) # type: ignore
914-
return self.expectation(*obs, reuse=reuse, **kws) # type: ignore
941+
return self.expectation(
942+
*obs, reuse=reuse, noise_conf=noise_conf, nmc=nmc, status=status, **kws # type: ignore
943+
)

tensorcircuit/basecircuit.py

+93-50
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from .simplify import _split_two_qubit_gate
2727
from .utils import arg_alias
2828

29+
2930
Gate = gates.Gate
3031
Tensor = Any
3132

@@ -613,6 +614,9 @@ def sample_expectation_ps(
613614
random_generator: Optional[Any] = None,
614615
status: Optional[Tensor] = None,
615616
readout_error: Optional[Sequence[Any]] = None,
617+
noise_conf: Optional[Any] = None,
618+
nmc: int = 1000,
619+
statusc: Optional[Tensor] = None,
616620
**kws: Any,
617621
) -> Tensor:
618622
"""
@@ -630,6 +634,22 @@ def sample_expectation_ps(
630634
>>> readout_error.append([0.4,0.7])
631635
>>> c.sample_expectation_ps(x=[0], y=[1],readout_error = readout_error)
632636
637+
>>> c = tc.Circuit(2)
638+
>>> c.cnot(0, 1)
639+
>>> c.rx(0, theta=0.4)
640+
>>> c.rx(1, theta=0.8)
641+
>>> c.h(0)
642+
>>> c.h(1)
643+
>>> error1 = tc.channels.generaldepolarizingchannel(0.1, 1)
644+
>>> error2 = tc.channels.generaldepolarizingchannel(0.06, 2)
645+
>>> readout_error = [[0.9, 0.75],[0.4, 0.7]]
646+
>>> noise_conf = NoiseConf()
647+
>>> noise_conf.add_noise("rx", error1)
648+
>>> noise_conf.add_noise("cnot", [error2], [[0, 1]])
649+
>>> noise_conf.add_noise("readout", readout_error)
650+
>>> c.sample_expectation_ps(x=[0], noise_conf=noise_conf, nmc=10000)
651+
0.44766843
652+
633653
:param x: index for Pauli X, defaults to None
634654
:type x: Optional[Sequence[int]], optional
635655
:param y: index for Pauli Y, defaults to None
@@ -645,62 +665,85 @@ def sample_expectation_ps(
645665
:type status: Optional[Tensor]
646666
:param readout_error: readout_error, defaults to None
647667
:type readout_error: Optional[Sequence[Any]]. Tensor, List, Tuple
668+
:param noise_conf: Noise Configuration, defaults to None
669+
:type noise_conf: Optional[NoiseConf], optional
670+
:param nmc: repetition time for Monte Carlo sampling for noisfy calculation, defaults to 1000
671+
:type nmc: int, optional
672+
:param statusc: external randomness given by tensor uniformly from [0, 1], defaults to None,
673+
used for noisfy circuit sampling
674+
:type statusc: Optional[Tensor], optional
648675
:return: [description]
649676
:rtype: Tensor
650677
"""
651-
inputs_nodes, _ = self._copy_state_tensor()
652-
inputs = inputs_nodes[0].tensor
653-
if self.is_dm is False:
654-
c = type(self)(self._nqubits, inputs=inputs) # type: ignore
655-
else:
656-
c = type(self)(self._nqubits, dminputs=inputs) # type: ignore
657-
if x is None:
658-
x = []
659-
if y is None:
660-
y = []
661-
if z is None:
662-
z = []
663-
for i in x:
664-
c.H(i) # type: ignore
665-
for i in y:
666-
c.rx(i, theta=np.pi / 2) # type: ignore
667-
s = c.state() # type: ignore
668-
if self.is_dm is False:
669-
p = backend.abs(s) ** 2
670-
else:
671-
p = backend.abs(backend.diagonal(s))
672-
673-
# readout error
674-
if readout_error is not None:
675-
p = self.readouterror_bs(readout_error, p)
676-
677-
x = list(x)
678-
y = list(y)
679-
z = list(z)
680-
if shots is None:
681-
mc = measurement_counts(
682-
p,
683-
counts=shots,
684-
format="count_vector",
685-
random_generator=random_generator,
686-
status=status,
687-
jittable=True,
688-
is_prob=True,
689-
)
690-
r = correlation_from_counts(x + y + z, mc)
678+
from .noisemodel import sample_expectation_ps_noisfy
679+
680+
if noise_conf is None:
681+
inputs_nodes, _ = self._copy_state_tensor()
682+
inputs = inputs_nodes[0].tensor
683+
if self.is_dm is False:
684+
c = type(self)(self._nqubits, inputs=inputs) # type: ignore
685+
else:
686+
c = type(self)(self._nqubits, dminputs=inputs) # type: ignore
687+
if x is None:
688+
x = []
689+
if y is None:
690+
y = []
691+
if z is None:
692+
z = []
693+
for i in x:
694+
c.H(i) # type: ignore
695+
for i in y:
696+
c.rx(i, theta=np.pi / 2) # type: ignore
697+
s = c.state() # type: ignore
698+
if self.is_dm is False:
699+
p = backend.abs(s) ** 2
700+
else:
701+
p = backend.abs(backend.diagonal(s))
702+
703+
# readout error
704+
if readout_error is not None:
705+
p = self.readouterror_bs(readout_error, p)
706+
707+
x = list(x)
708+
y = list(y)
709+
z = list(z)
710+
if shots is None:
711+
mc = measurement_counts(
712+
p,
713+
counts=shots,
714+
format="count_vector",
715+
random_generator=random_generator,
716+
status=status,
717+
jittable=True,
718+
is_prob=True,
719+
)
720+
r = correlation_from_counts(x + y + z, mc)
721+
else:
722+
mc = measurement_counts(
723+
p,
724+
counts=shots,
725+
format="sample_bin",
726+
random_generator=random_generator,
727+
status=status,
728+
jittable=True,
729+
is_prob=True,
730+
)
731+
r = correlation_from_samples(x + y + z, mc, self._nqubits)
732+
# TODO(@refraction-ray): analytical standard deviation
733+
return r
691734
else:
692-
mc = measurement_counts(
693-
p,
694-
counts=shots,
695-
format="sample_bin",
696-
random_generator=random_generator,
735+
return sample_expectation_ps_noisfy(
736+
c=self,
737+
x=x,
738+
y=y,
739+
z=z,
740+
noise_conf=noise_conf,
741+
nmc=nmc,
742+
shots=shots,
743+
statusc=statusc,
697744
status=status,
698-
jittable=True,
699-
is_prob=True,
745+
**kws,
700746
)
701-
r = correlation_from_samples(x + y + z, mc, self._nqubits)
702-
# TODO(@refraction-ray): analytical standard deviation
703-
return r
704747

705748
sexpps = sample_expectation_ps
706749

tensorcircuit/channels.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,6 @@ def composedkraus(kraus1: KrausList, kraus2: KrausList) -> KrausList:
982982
new_kraus.append(k)
983983
return KrausList(
984984
new_kraus,
985-
name="composed_channel",
985+
name=kraus1.name + "_" + kraus2.name,
986986
is_unitary=kraus1.is_unitary and kraus2.is_unitary,
987987
)

tensorcircuit/circuit.py

+49-12
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,10 @@ def expectation( # type: ignore
772772
*ops: Tuple[tn.Node, List[int]],
773773
reuse: bool = True,
774774
enable_lightcone: bool = False,
775+
noise_conf: Optional[Any] = None,
776+
nmc: int = 1000,
777+
status: Optional[Tensor] = None,
778+
**kws: Any,
775779
) -> Tensor:
776780
"""
777781
Compute the expectation of corresponding operators.
@@ -783,6 +787,20 @@ def expectation( # type: ignore
783787
>>> c.expectation((tc.gates.z(), [0]))
784788
array(0.+0.j, dtype=complex64)
785789
790+
>>> c = tc.Circuit(2)
791+
>>> c.cnot(0, 1)
792+
>>> c.rx(0, theta=0.4)
793+
>>> c.rx(1, theta=0.8)
794+
>>> c.h(0)
795+
>>> c.h(1)
796+
>>> error1 = tc.channels.generaldepolarizingchannel(0.1, 1)
797+
>>> error2 = tc.channels.generaldepolarizingchannel(0.06, 2)
798+
>>> noise_conf = NoiseConf()
799+
>>> noise_conf.add_noise("rx", error1)
800+
>>> noise_conf.add_noise("cnot", [error2], [[0, 1]])
801+
>>> c.expectation((tc.gates.x(), [0]), noise_conf=noise_conf, nmc=10000)
802+
(0.46274087-3.764033e-09j)
803+
786804
:param ops: Operator and its position on the circuit,
787805
eg. ``(tc.gates.z(), [1, ]), (tc.gates.x(), [2, ])`` is for operator :math:`Z_1X_2`.
788806
:type ops: Tuple[tn.Node, List[int]]
@@ -791,22 +809,41 @@ def expectation( # type: ignore
791809
:type reuse: bool, optional
792810
:param enable_lightcone: whether enable light cone simplification, defaults to False
793811
:type enable_lightcone: bool, optional
812+
:param noise_conf: Noise Configuration, defaults to None
813+
:type noise_conf: Optional[NoiseConf], optional
814+
:param nmc: repetition time for Monte Carlo sampling for noisfy calculation, defaults to 1000
815+
:type nmc: int, optional
816+
:param status: external randomness given by tensor uniformly from [0, 1], defaults to None,
817+
used for noisfy circuit sampling
818+
:type status: Optional[Tensor], optional
794819
:raises ValueError: "Cannot measure two operators in one index"
795820
:return: Tensor with one element
796821
:rtype: Tensor
797822
"""
798-
# if not reuse:
799-
# nodes1, edge1 = self._copy()
800-
# nodes2, edge2 = self._copy(conj=True)
801-
# else: # reuse
802-
803-
# self._nodes = nodes1
804-
if enable_lightcone:
805-
reuse = False
806-
nodes1 = self.expectation_before(*ops, reuse=reuse)
807-
if enable_lightcone:
808-
nodes1 = _full_light_cone_cancel(nodes1)
809-
return contractor(nodes1).tensor
823+
from .noisemodel import expectation_noisfy
824+
825+
if noise_conf is None:
826+
# if not reuse:
827+
# nodes1, edge1 = self._copy()
828+
# nodes2, edge2 = self._copy(conj=True)
829+
# else: # reuse
830+
831+
# self._nodes = nodes1
832+
if enable_lightcone:
833+
reuse = False
834+
nodes1 = self.expectation_before(*ops, reuse=reuse)
835+
if enable_lightcone:
836+
nodes1 = _full_light_cone_cancel(nodes1)
837+
return contractor(nodes1).tensor
838+
else:
839+
return expectation_noisfy(
840+
self,
841+
*ops,
842+
noise_conf=noise_conf,
843+
nmc=nmc,
844+
status=status,
845+
**kws,
846+
)
810847

811848

812849
Circuit._meta_apply()

tensorcircuit/densitymatrix.py

+28-3
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,13 @@ def get_dm_as_quoperator(self) -> QuOperator:
265265
return QuOperator(edges[: self._nqubits], edges[self._nqubits :])
266266

267267
def expectation(
268-
self, *ops: Tuple[tn.Node, List[int]], reuse: bool = True, **kws: Any
268+
self,
269+
*ops: Tuple[tn.Node, List[int]],
270+
reuse: bool = True,
271+
noise_conf: Optional[Any] = None,
272+
nmc: int = 1000,
273+
status: Optional[Tensor] = None,
274+
**kws: Any
269275
) -> tn.Node.tensor:
270276
"""
271277
Compute the expectation of corresponding operators.
@@ -275,11 +281,30 @@ def expectation(
275281
:type ops: Tuple[tn.Node, List[int]]
276282
:param reuse: whether contract the density matrix in advance, defaults to True
277283
:type reuse: bool
284+
:param noise_conf: Noise Configuration, defaults to None
285+
:type noise_conf: Optional[NoiseConf], optional
286+
:param nmc: repetition time for Monte Carlo sampling for noisfy calculation, defaults to 1000
287+
:type nmc: int
288+
:param status: external randomness given by tensor uniformly from [0, 1], defaults to None,
289+
used for noisfy circuit sampling
290+
:type status: Optional[Tensor], optional
278291
:return: Tensor with one element
279292
:rtype: Tensor
280293
"""
281-
nodes = self.expectation_before(*ops, reuse=reuse)
282-
return contractor(nodes).tensor
294+
from .noisemodel import expectation_noisfy
295+
296+
if noise_conf is None:
297+
nodes = self.expectation_before(*ops, reuse=reuse)
298+
return contractor(nodes).tensor
299+
else:
300+
return expectation_noisfy(
301+
self,
302+
*ops,
303+
noise_conf=noise_conf,
304+
nmc=nmc,
305+
status=status,
306+
**kws,
307+
)
283308

284309
@staticmethod
285310
def check_density_matrix(dm: Tensor) -> None:

tensorcircuit/mpscircuit.py

+1
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,7 @@ def expectation( # type: ignore
821821
conj: bool = True,
822822
normalize: bool = False,
823823
split: Optional[Dict[str, Any]] = None,
824+
**kws: Any,
824825
) -> Tensor:
825826
"""
826827
Compute the expectation of corresponding operators in the form of tensor.

0 commit comments

Comments
 (0)