tensorcircuit-nightly 1.0.2.dev20250108__py3-none-any.whl → 1.4.0.dev20251103__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tensorcircuit-nightly might be problematic. Click here for more details.
- tensorcircuit/__init__.py +18 -2
- tensorcircuit/about.py +46 -0
- tensorcircuit/abstractcircuit.py +4 -0
- tensorcircuit/analogcircuit.py +413 -0
- tensorcircuit/applications/layers.py +1 -1
- tensorcircuit/applications/van.py +1 -1
- tensorcircuit/backends/abstract_backend.py +320 -7
- tensorcircuit/backends/cupy_backend.py +3 -1
- tensorcircuit/backends/jax_backend.py +102 -4
- tensorcircuit/backends/jax_ops.py +110 -1
- tensorcircuit/backends/numpy_backend.py +49 -3
- tensorcircuit/backends/pytorch_backend.py +92 -3
- tensorcircuit/backends/tensorflow_backend.py +102 -3
- tensorcircuit/basecircuit.py +157 -98
- tensorcircuit/circuit.py +115 -57
- tensorcircuit/cloud/local.py +1 -1
- tensorcircuit/cloud/quafu_provider.py +1 -1
- tensorcircuit/cloud/tencent.py +1 -1
- tensorcircuit/compiler/simple_compiler.py +2 -2
- tensorcircuit/cons.py +142 -21
- tensorcircuit/densitymatrix.py +43 -14
- tensorcircuit/experimental.py +387 -129
- tensorcircuit/fgs.py +282 -81
- tensorcircuit/gates.py +66 -22
- tensorcircuit/interfaces/__init__.py +1 -3
- tensorcircuit/interfaces/jax.py +189 -0
- tensorcircuit/keras.py +3 -3
- tensorcircuit/mpscircuit.py +154 -65
- tensorcircuit/quantum.py +868 -152
- tensorcircuit/quditcircuit.py +733 -0
- tensorcircuit/quditgates.py +618 -0
- tensorcircuit/results/counts.py +147 -20
- tensorcircuit/results/readout_mitigation.py +4 -1
- tensorcircuit/shadows.py +1 -1
- tensorcircuit/simplify.py +3 -1
- tensorcircuit/stabilizercircuit.py +479 -0
- tensorcircuit/templates/__init__.py +2 -0
- tensorcircuit/templates/blocks.py +2 -2
- tensorcircuit/templates/hamiltonians.py +174 -0
- tensorcircuit/templates/lattice.py +1789 -0
- tensorcircuit/timeevol.py +896 -0
- tensorcircuit/translation.py +10 -3
- tensorcircuit/utils.py +7 -0
- {tensorcircuit_nightly-1.0.2.dev20250108.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info}/METADATA +73 -23
- tensorcircuit_nightly-1.4.0.dev20251103.dist-info/RECORD +96 -0
- {tensorcircuit_nightly-1.0.2.dev20250108.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info}/WHEEL +1 -1
- {tensorcircuit_nightly-1.0.2.dev20250108.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info}/top_level.txt +0 -1
- tensorcircuit_nightly-1.0.2.dev20250108.dist-info/RECORD +0 -115
- tests/__init__.py +0 -0
- tests/conftest.py +0 -67
- tests/test_backends.py +0 -1031
- tests/test_calibrating.py +0 -149
- tests/test_channels.py +0 -365
- tests/test_circuit.py +0 -1699
- tests/test_cloud.py +0 -219
- tests/test_compiler.py +0 -147
- tests/test_dmcircuit.py +0 -555
- tests/test_ensemble.py +0 -72
- tests/test_fgs.py +0 -310
- tests/test_gates.py +0 -156
- tests/test_interfaces.py +0 -429
- tests/test_keras.py +0 -160
- tests/test_miscs.py +0 -277
- tests/test_mpscircuit.py +0 -341
- tests/test_noisemodel.py +0 -156
- tests/test_qaoa.py +0 -86
- tests/test_qem.py +0 -152
- tests/test_quantum.py +0 -526
- tests/test_quantum_attr.py +0 -42
- tests/test_results.py +0 -347
- tests/test_shadows.py +0 -160
- tests/test_simplify.py +0 -46
- tests/test_templates.py +0 -218
- tests/test_torchnn.py +0 -99
- tests/test_van.py +0 -102
- {tensorcircuit_nightly-1.0.2.dev20250108.dist-info → tensorcircuit_nightly-1.4.0.dev20251103.dist-info/licenses}/LICENSE +0 -0
tensorcircuit/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
__version__ = "1.0.
|
|
1
|
+
__version__ = "1.4.0.dev20251103"
|
|
2
2
|
__author__ = "TensorCircuit Authors"
|
|
3
3
|
__creator__ = "refraction-ray"
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ from .utils import gpu_memory_share
|
|
|
6
6
|
|
|
7
7
|
gpu_memory_share()
|
|
8
8
|
|
|
9
|
-
from .about import about
|
|
9
|
+
from .about import about, cite
|
|
10
10
|
from .cons import (
|
|
11
11
|
backend,
|
|
12
12
|
set_backend,
|
|
@@ -23,14 +23,27 @@ from .cons import (
|
|
|
23
23
|
runtime_contractor,
|
|
24
24
|
) # prerun of set hooks
|
|
25
25
|
from . import gates
|
|
26
|
+
from . import quditgates
|
|
26
27
|
from . import basecircuit
|
|
27
28
|
from .gates import Gate
|
|
29
|
+
from .quditcircuit import QuditCircuit
|
|
30
|
+
from .analogcircuit import AnalogCircuit
|
|
28
31
|
from .circuit import Circuit, expectation
|
|
29
32
|
from .mpscircuit import MPSCircuit
|
|
30
33
|
from .densitymatrix import DMCircuit as DMCircuit_reference
|
|
31
34
|
from .densitymatrix import DMCircuit2
|
|
32
35
|
|
|
33
36
|
DMCircuit = DMCircuit2 # compatibility issue to still expose DMCircuit2
|
|
37
|
+
DensityMatrixCircuit = DMCircuit
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
from .stabilizercircuit import StabilizerCircuit
|
|
41
|
+
|
|
42
|
+
CliffordCircuit = StabilizerCircuit
|
|
43
|
+
StabCircuit = StabilizerCircuit
|
|
44
|
+
except ModuleNotFoundError:
|
|
45
|
+
pass
|
|
46
|
+
|
|
34
47
|
from .gates import num_to_tensor, array_to_tensor
|
|
35
48
|
from .vis import qir2tex, render_pdf
|
|
36
49
|
from . import interfaces
|
|
@@ -42,6 +55,9 @@ from . import compiler
|
|
|
42
55
|
from . import cloud
|
|
43
56
|
from . import fgs
|
|
44
57
|
from .fgs import FGSSimulator
|
|
58
|
+
from . import timeevol
|
|
59
|
+
|
|
60
|
+
FGSCircuit = FGSSimulator
|
|
45
61
|
|
|
46
62
|
try:
|
|
47
63
|
from . import keras
|
tensorcircuit/about.py
CHANGED
|
@@ -23,6 +23,8 @@ def about() -> None:
|
|
|
23
23
|
print(f"Scipy version: {scipy.__version__}")
|
|
24
24
|
except ModuleNotFoundError:
|
|
25
25
|
print(f"Scipy is not installed")
|
|
26
|
+
except Exception as e:
|
|
27
|
+
print(f"Misconfiguration for Scipy: {e}")
|
|
26
28
|
|
|
27
29
|
try:
|
|
28
30
|
import pandas
|
|
@@ -30,6 +32,8 @@ def about() -> None:
|
|
|
30
32
|
print(f"Pandas version: {pandas.__version__}")
|
|
31
33
|
except ModuleNotFoundError:
|
|
32
34
|
print(f"Pandas is not installed")
|
|
35
|
+
except Exception as e:
|
|
36
|
+
print(f"Misconfiguration for Pandas: {e}")
|
|
33
37
|
|
|
34
38
|
try:
|
|
35
39
|
import tensornetwork as tn
|
|
@@ -37,6 +41,8 @@ def about() -> None:
|
|
|
37
41
|
print(f"TensorNetwork version: {tn.__version__}")
|
|
38
42
|
except ModuleNotFoundError:
|
|
39
43
|
print(f"TensorNetwork is not installed")
|
|
44
|
+
except Exception as e:
|
|
45
|
+
print(f"Misconfiguration for TensorNetwork: {e}")
|
|
40
46
|
|
|
41
47
|
try:
|
|
42
48
|
import cotengra
|
|
@@ -47,6 +53,8 @@ def about() -> None:
|
|
|
47
53
|
print(f"Cotengra: installed")
|
|
48
54
|
except ModuleNotFoundError:
|
|
49
55
|
print(f"Cotengra is not installed")
|
|
56
|
+
except Exception as e:
|
|
57
|
+
print(f"Misconfiguration for Cotengra: {e}")
|
|
50
58
|
|
|
51
59
|
try:
|
|
52
60
|
import tensorflow as tf
|
|
@@ -56,6 +64,8 @@ def about() -> None:
|
|
|
56
64
|
print(f"TensorFlow CUDA infos: {dict(tf.sysconfig.get_build_info())}")
|
|
57
65
|
except ModuleNotFoundError:
|
|
58
66
|
print(f"TensorFlow is not installed")
|
|
67
|
+
except Exception as e:
|
|
68
|
+
print(f"Misconfiguration for TensorFlow: {e}")
|
|
59
69
|
|
|
60
70
|
try:
|
|
61
71
|
import jax
|
|
@@ -68,6 +78,8 @@ def about() -> None:
|
|
|
68
78
|
print(f"Jax installation doesn't support GPU")
|
|
69
79
|
except ModuleNotFoundError:
|
|
70
80
|
print(f"Jax is not installed")
|
|
81
|
+
except Exception as e:
|
|
82
|
+
print(f"Misconfiguration for Jax: {e}")
|
|
71
83
|
|
|
72
84
|
try:
|
|
73
85
|
import jaxlib
|
|
@@ -75,6 +87,8 @@ def about() -> None:
|
|
|
75
87
|
print(f"JaxLib version: {jaxlib.__version__}")
|
|
76
88
|
except ModuleNotFoundError:
|
|
77
89
|
print(f"JaxLib is not installed")
|
|
90
|
+
except Exception as e:
|
|
91
|
+
print(f"Misconfiguration for Jaxlib: {e}")
|
|
78
92
|
|
|
79
93
|
try:
|
|
80
94
|
import torch
|
|
@@ -88,6 +102,8 @@ def about() -> None:
|
|
|
88
102
|
print(f"Pytorch cuda version: {torch.version.cuda}")
|
|
89
103
|
except ModuleNotFoundError:
|
|
90
104
|
print(f"PyTorch is not installed")
|
|
105
|
+
except Exception as e:
|
|
106
|
+
print(f"Misconfiguration for Torch: {e}")
|
|
91
107
|
|
|
92
108
|
try:
|
|
93
109
|
import cupy
|
|
@@ -95,6 +111,8 @@ def about() -> None:
|
|
|
95
111
|
print(f"Cupy version: {cupy.__version__}")
|
|
96
112
|
except ModuleNotFoundError:
|
|
97
113
|
print(f"Cupy is not installed")
|
|
114
|
+
except Exception as e:
|
|
115
|
+
print(f"Misconfiguration for Cupy: {e}")
|
|
98
116
|
|
|
99
117
|
try:
|
|
100
118
|
import qiskit
|
|
@@ -102,6 +120,8 @@ def about() -> None:
|
|
|
102
120
|
print(f"Qiskit version: {qiskit.__version__}")
|
|
103
121
|
except ModuleNotFoundError:
|
|
104
122
|
print(f"Qiskit is not installed")
|
|
123
|
+
except Exception as e:
|
|
124
|
+
print(f"Misconfiguration for Qiskit: {e}")
|
|
105
125
|
|
|
106
126
|
try:
|
|
107
127
|
import cirq
|
|
@@ -109,11 +129,37 @@ def about() -> None:
|
|
|
109
129
|
print(f"Cirq version: {cirq.__version__}")
|
|
110
130
|
except ModuleNotFoundError:
|
|
111
131
|
print(f"Cirq is not installed")
|
|
132
|
+
except Exception as e:
|
|
133
|
+
print(f"Misconfiguration for Cirq: {e}")
|
|
112
134
|
|
|
113
135
|
from tensorcircuit import __version__
|
|
114
136
|
|
|
115
137
|
print(f"TensorCircuit version {__version__}")
|
|
116
138
|
|
|
117
139
|
|
|
140
|
+
def cite(format: str = "bib") -> str:
|
|
141
|
+
"""
|
|
142
|
+
Returns the citation information for tensorcircuit.
|
|
143
|
+
Please cite our work if you use the package in your research.
|
|
144
|
+
|
|
145
|
+
:param format: format for bib, defaults to "bib"
|
|
146
|
+
:type format: str, optional
|
|
147
|
+
:return: the citation information
|
|
148
|
+
:rtype: str
|
|
149
|
+
"""
|
|
150
|
+
if format == "bib":
|
|
151
|
+
return """@article{Zhang_TensorCircuit_2023,
|
|
152
|
+
author = {Zhang, Shi-Xin and Allcock, Jonathan and Wan, Zhou-Quan and Liu, Shuo and Sun, Jiace and Yu, Hao and Yang, Xing-Han and Qiu, Jiezhong and Ye, Zhaofeng and Chen, Yu-Qin and Lee, Chee-Kong and Zheng, Yi-Cong and Jian, Shao-Kai and Yao, Hong and Hsieh, Chang-Yu and Zhang, Shengyu},
|
|
153
|
+
doi = {10.22331/q-2023-02-02-912},
|
|
154
|
+
journal = {Quantum},
|
|
155
|
+
month = feb,
|
|
156
|
+
title = {{TensorCircuit: a Quantum Software Framework for the NISQ Era}},
|
|
157
|
+
volume = {7},
|
|
158
|
+
year = {2023}}"""
|
|
159
|
+
elif format == "aps":
|
|
160
|
+
return """S.-X. Zhang, J. Allcock, Z.-Q. Wan, S. Liu, J. Sun, H. Yu, X.-H. Yang, J. Qiu, Z. Ye, Y.-Q. Chen, C.-K. Lee, Y.-C. Zheng, S.-K. Jian, H. Yao, C.-Y. Hsieh, and S. Zhang, TensorCircuit: a Quantum Software Framework for the NISQ Era, Quantum 7, 912 (2023).""" # pylint: disable=line-too-long
|
|
161
|
+
raise ValueError(f"Unsupported format: {format}")
|
|
162
|
+
|
|
163
|
+
|
|
118
164
|
if __name__ == "__main__":
|
|
119
165
|
about()
|
tensorcircuit/abstractcircuit.py
CHANGED
|
@@ -53,6 +53,7 @@ vgates = [
|
|
|
53
53
|
"any",
|
|
54
54
|
"exp",
|
|
55
55
|
"exp1",
|
|
56
|
+
"su4",
|
|
56
57
|
]
|
|
57
58
|
mpogates = ["multicontrol", "mpo"]
|
|
58
59
|
gate_aliases = [
|
|
@@ -65,9 +66,12 @@ gate_aliases = [
|
|
|
65
66
|
["td", "tdg"],
|
|
66
67
|
]
|
|
67
68
|
|
|
69
|
+
defined_gates = sgates + vgates + mpogates + [alias[1] for alias in gate_aliases]
|
|
70
|
+
|
|
68
71
|
|
|
69
72
|
class AbstractCircuit:
|
|
70
73
|
_nqubits: int
|
|
74
|
+
_d: int = 2
|
|
71
75
|
_qir: List[Dict[str, Any]]
|
|
72
76
|
_extra_qir: List[Dict[str, Any]]
|
|
73
77
|
inputs: Tensor
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Analog-Digital Hybrid Circuit class wrapper
|
|
3
|
+
only support jax backend
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any, List, Optional, Callable, Dict, Tuple, Union, Sequence
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from functools import partial
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import tensornetwork as tn
|
|
12
|
+
|
|
13
|
+
from .cons import backend, rdtypestr
|
|
14
|
+
from .abstractcircuit import defined_gates
|
|
15
|
+
from .circuit import Circuit
|
|
16
|
+
from .quantum import QuOperator
|
|
17
|
+
from .timeevol import ode_evol_global, ode_evol_local
|
|
18
|
+
from .utils import arg_alias
|
|
19
|
+
|
|
20
|
+
Tensor = Any
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class AnalogBlock:
|
|
25
|
+
"""
|
|
26
|
+
A data structure to hold information about an analog evolution block.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
hamiltonian_func: Callable[[Tensor], Tensor]
|
|
30
|
+
time: float
|
|
31
|
+
index: Optional[List[int]] = None
|
|
32
|
+
solver_options: Optional[Dict[str, Any]] = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AnalogCircuit:
|
|
36
|
+
"""
|
|
37
|
+
A class for hybrid digital-analog quantum simulation with time-dependent Hamiltonians.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
nqubits: int,
|
|
43
|
+
inputs: Optional[Tensor] = None,
|
|
44
|
+
mps_inputs: Optional[QuOperator] = None,
|
|
45
|
+
split: Optional[Dict[str, Any]] = None,
|
|
46
|
+
dim: Optional[int] = None,
|
|
47
|
+
):
|
|
48
|
+
"""
|
|
49
|
+
Initializes the hybrid circuit.
|
|
50
|
+
|
|
51
|
+
:param nqubits: The number of qubits in the circuit.
|
|
52
|
+
:type nqubits: int
|
|
53
|
+
:param dim: The local Hilbert space dimension per site. Qudit is supported for 2 <= d <= 36.
|
|
54
|
+
:type dim: If None, the dimension of the circuit will be `2`, which is a qubit system.
|
|
55
|
+
:param inputs: If not None, the initial state of the circuit is taken as ``inputs``
|
|
56
|
+
instead of :math:`\vert 0 \rangle^n` qubits, defaults to None.
|
|
57
|
+
:type inputs: Optional[Tensor], optional
|
|
58
|
+
:param mps_inputs: QuVector for a MPS like initial wavefunction.
|
|
59
|
+
:type mps_inputs: Optional[QuOperator]
|
|
60
|
+
:param split: dict if two qubit gate is ready for split, including parameters for at least one of
|
|
61
|
+
``max_singular_values`` and ``max_truncation_err``.
|
|
62
|
+
:type split: Optional[Dict[str, Any]]
|
|
63
|
+
"""
|
|
64
|
+
self.num_qubits, self._nqubits = nqubits, nqubits
|
|
65
|
+
self.dim = 2**self.num_qubits
|
|
66
|
+
self.inputs = inputs
|
|
67
|
+
if inputs is None:
|
|
68
|
+
self.inputs = np.zeros([self.dim])
|
|
69
|
+
self.inputs[0] = 1.0
|
|
70
|
+
self.inputs = backend.convert_to_tensor(self.inputs)
|
|
71
|
+
|
|
72
|
+
# List of digital circuits, starting with one empty circuit.
|
|
73
|
+
self.digital_circuits: List[Circuit] = [
|
|
74
|
+
Circuit(self.num_qubits, inputs, mps_inputs, split, dim)
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
# List of analog blocks, each containing the Hamiltonian function, time, and solver options.
|
|
78
|
+
self.analog_blocks: List[AnalogBlock] = []
|
|
79
|
+
self._effective_circuit: Optional[Circuit] = None
|
|
80
|
+
self._solver_options: Dict[str, Any] = {}
|
|
81
|
+
|
|
82
|
+
def set_solver_options(self, **kws: Any) -> None:
|
|
83
|
+
"""
|
|
84
|
+
set solver options globally for this circuit object
|
|
85
|
+
"""
|
|
86
|
+
self._solver_options = kws
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def effective_circuit(self) -> Circuit:
|
|
90
|
+
"""
|
|
91
|
+
Returns the effective circuit after all blocks have been added.
|
|
92
|
+
"""
|
|
93
|
+
if self._effective_circuit is None:
|
|
94
|
+
self.state()
|
|
95
|
+
return self._effective_circuit # type: ignore
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def current_digital_circuit(self) -> Circuit:
|
|
99
|
+
"""
|
|
100
|
+
Returns the last (currently active) digital circuit.
|
|
101
|
+
"""
|
|
102
|
+
return self.digital_circuits[-1]
|
|
103
|
+
|
|
104
|
+
def add_analog_block(
|
|
105
|
+
self,
|
|
106
|
+
hamiltonian: Callable[[float], Tensor],
|
|
107
|
+
time: Union[float, List[Tensor]],
|
|
108
|
+
index: Optional[List[int]] = None,
|
|
109
|
+
**solver_options: Any,
|
|
110
|
+
) -> "AnalogCircuit":
|
|
111
|
+
"""
|
|
112
|
+
Adds a time-dependent analog evolution block to the circuit.
|
|
113
|
+
|
|
114
|
+
This finalizes the current digital block and prepares a new one for subsequent gates.
|
|
115
|
+
|
|
116
|
+
:param hamiltonian_func: A function H(t) that takes a time `t` (from 0 to `time`)
|
|
117
|
+
and returns the Hamiltonian matrix at that instant.
|
|
118
|
+
:type hamiltonian_func: Callable[[float], np.ndarray]
|
|
119
|
+
:param time: The total evolution time 'T'.
|
|
120
|
+
:type time: float
|
|
121
|
+
:param index: The indices of the qubits to apply the analog evolution to. Defaults None for
|
|
122
|
+
global application.
|
|
123
|
+
:type index: Optional[List[int]]
|
|
124
|
+
:param solver_options: Keyword arguments passed directly to `tc.timeevol.ode_evolve`
|
|
125
|
+
:type solver_options: Dict[str, Any]
|
|
126
|
+
"""
|
|
127
|
+
# Create and store the analog block information
|
|
128
|
+
time = backend.convert_to_tensor(time, dtype=rdtypestr)
|
|
129
|
+
time = backend.reshape(time, [-1])
|
|
130
|
+
if backend.shape_tuple(time)[0] == 1:
|
|
131
|
+
time = backend.stack([0.0, time[0]]) # type: ignore
|
|
132
|
+
elif backend.shape_tuple(time)[0] > 2:
|
|
133
|
+
raise ValueError(
|
|
134
|
+
"Time must be a scalar or a two elements array for the starting and end points."
|
|
135
|
+
)
|
|
136
|
+
combined_solver_options = self._solver_options.copy()
|
|
137
|
+
combined_solver_options.update(solver_options)
|
|
138
|
+
block = AnalogBlock(
|
|
139
|
+
hamiltonian_func=hamiltonian,
|
|
140
|
+
time=time, # type: ignore
|
|
141
|
+
index=index,
|
|
142
|
+
solver_options=combined_solver_options,
|
|
143
|
+
)
|
|
144
|
+
self.analog_blocks.append(block)
|
|
145
|
+
|
|
146
|
+
# After adding an analog block, we start a new digital block.
|
|
147
|
+
self.digital_circuits.append(Circuit(self.num_qubits, inputs=self.inputs))
|
|
148
|
+
self._effective_circuit = None
|
|
149
|
+
return self # Allow for chaining
|
|
150
|
+
|
|
151
|
+
def __getattr__(self, name: str) -> Any:
|
|
152
|
+
"""
|
|
153
|
+
Metaprogramming to forward gate calls to the current digital circuit.
|
|
154
|
+
This enables syntax like `analog_circuit.h(0)`.
|
|
155
|
+
"""
|
|
156
|
+
gate_method = getattr(self.current_digital_circuit, name, None)
|
|
157
|
+
|
|
158
|
+
if gate_method and callable(gate_method) and name.lower() in defined_gates:
|
|
159
|
+
|
|
160
|
+
def wrapper(*args, **kwargs): # type: ignore
|
|
161
|
+
gate_method(*args, **kwargs)
|
|
162
|
+
self._effective_circuit = None
|
|
163
|
+
return self
|
|
164
|
+
|
|
165
|
+
return wrapper
|
|
166
|
+
else:
|
|
167
|
+
raise AttributeError(
|
|
168
|
+
f"'{type(self).__name__}' object or its underlying '{type(self.current_digital_circuit).__name__}' "
|
|
169
|
+
f"object has no attribute '{name}'."
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def state(self) -> Tensor:
|
|
173
|
+
"""
|
|
174
|
+
Executes the full digital-analog sequence.
|
|
175
|
+
|
|
176
|
+
:return: The final state vector after the full evolution
|
|
177
|
+
:rtype: Tensor
|
|
178
|
+
"""
|
|
179
|
+
# Propagate the state through the alternating circuit blocks
|
|
180
|
+
for i, analog_block in enumerate(self.analog_blocks):
|
|
181
|
+
# 1. Apply Digital Block i
|
|
182
|
+
digital_c = self.digital_circuits[i]
|
|
183
|
+
if i > 0:
|
|
184
|
+
digital_c.replace_inputs(psi) # type: ignore
|
|
185
|
+
psi = digital_c.wavefunction()
|
|
186
|
+
|
|
187
|
+
if analog_block.index is None:
|
|
188
|
+
psi = ode_evol_global( # type: ignore
|
|
189
|
+
hamiltonian=analog_block.hamiltonian_func,
|
|
190
|
+
initial_state=psi,
|
|
191
|
+
times=analog_block.time,
|
|
192
|
+
**analog_block.solver_options,
|
|
193
|
+
)
|
|
194
|
+
else:
|
|
195
|
+
psi = ode_evol_local( # type: ignore
|
|
196
|
+
hamiltonian=analog_block.hamiltonian_func,
|
|
197
|
+
initial_state=psi,
|
|
198
|
+
times=analog_block.time,
|
|
199
|
+
index=analog_block.index,
|
|
200
|
+
**analog_block.solver_options,
|
|
201
|
+
)
|
|
202
|
+
psi = psi[-1]
|
|
203
|
+
# TODO(@refraction-ray): support more time evol methods
|
|
204
|
+
|
|
205
|
+
# 3. Apply the final digital circuit
|
|
206
|
+
if self.analog_blocks:
|
|
207
|
+
self.digital_circuits[-1].replace_inputs(psi)
|
|
208
|
+
psi = self.digital_circuits[-1].wavefunction()
|
|
209
|
+
else:
|
|
210
|
+
psi = self.digital_circuits[-1].wavefunction()
|
|
211
|
+
self._effective_circuit = Circuit(self.num_qubits, inputs=psi)
|
|
212
|
+
|
|
213
|
+
return psi
|
|
214
|
+
|
|
215
|
+
wavefunction = state
|
|
216
|
+
|
|
217
|
+
def expectation(
|
|
218
|
+
self,
|
|
219
|
+
*ops: Tuple[tn.Node, List[int]],
|
|
220
|
+
reuse: bool = True,
|
|
221
|
+
enable_lightcone: bool = False,
|
|
222
|
+
nmc: int = 1000,
|
|
223
|
+
**kws: Any,
|
|
224
|
+
) -> Tensor:
|
|
225
|
+
"""
|
|
226
|
+
Compute expectation(s) of local operators.
|
|
227
|
+
|
|
228
|
+
:param ops: Pairs of `(operator_node, [sites])` specifying where each operator acts.
|
|
229
|
+
:type ops: Tuple[tn.Node, List[int]]
|
|
230
|
+
:param reuse: If True, then the wavefunction tensor is cached for further expectation evaluation,
|
|
231
|
+
defaults to be true.
|
|
232
|
+
:type reuse: bool, optional
|
|
233
|
+
:param enable_lightcone: whether enable light cone simplification, defaults to False
|
|
234
|
+
:type enable_lightcone: bool, optional
|
|
235
|
+
:param nmc: repetition time for Monte Carlo sampling for noisfy calculation, defaults to 1000
|
|
236
|
+
:type nmc: int, optional
|
|
237
|
+
:return: Tensor with one element
|
|
238
|
+
:rtype: Tensor
|
|
239
|
+
"""
|
|
240
|
+
return self.effective_circuit.expectation(
|
|
241
|
+
*ops,
|
|
242
|
+
reuse=reuse,
|
|
243
|
+
enable_lightcone=enable_lightcone,
|
|
244
|
+
noise_conf=None,
|
|
245
|
+
nmc=nmc,
|
|
246
|
+
**kws,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
def measure_jit(
|
|
250
|
+
self, *index: int, with_prob: bool = False, status: Optional[Tensor] = None
|
|
251
|
+
) -> Tuple[Tensor, Tensor]:
|
|
252
|
+
"""
|
|
253
|
+
Take measurement on the given site indices (computational basis).
|
|
254
|
+
This method is jittable!
|
|
255
|
+
|
|
256
|
+
:param index: Measure on which site (wire) index.
|
|
257
|
+
:type index: int
|
|
258
|
+
:param with_prob: If true, theoretical probability is also returned.
|
|
259
|
+
:type with_prob: bool, optional
|
|
260
|
+
:param status: external randomness, with shape [index], defaults to None
|
|
261
|
+
:type status: Optional[Tensor]
|
|
262
|
+
:return: The sample output and probability (optional) of the quantum line.
|
|
263
|
+
:rtype: Tuple[Tensor, Tensor]
|
|
264
|
+
"""
|
|
265
|
+
return self.effective_circuit.measure_jit(
|
|
266
|
+
*index, with_prob=with_prob, status=status
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
measure = measure_jit
|
|
270
|
+
|
|
271
|
+
def amplitude(self, l: Union[str, Tensor]) -> Tensor:
|
|
272
|
+
r"""
|
|
273
|
+
Return the amplitude for a given bitstring `l`.
|
|
274
|
+
|
|
275
|
+
For state simulators, this computes :math:`\langle l \vert \psi \rangle`.
|
|
276
|
+
|
|
277
|
+
:param l: Bitstring in base-`d` using `0-9A-Z`.
|
|
278
|
+
:type l: Union[str, Tensor]
|
|
279
|
+
:return: Complex amplitude.
|
|
280
|
+
:rtype: Tensor
|
|
281
|
+
"""
|
|
282
|
+
return self.effective_circuit.amplitude(l)
|
|
283
|
+
|
|
284
|
+
def probability(self) -> Tensor:
|
|
285
|
+
"""
|
|
286
|
+
Get the length-`2^n` probability vector over the computational basis.
|
|
287
|
+
|
|
288
|
+
:return: Probability vector of shape `[dim^n]`.
|
|
289
|
+
:rtype: Tensor
|
|
290
|
+
"""
|
|
291
|
+
return self.effective_circuit.probability()
|
|
292
|
+
|
|
293
|
+
def expectation_ps(
|
|
294
|
+
self,
|
|
295
|
+
x: Optional[Sequence[int]] = None,
|
|
296
|
+
y: Optional[Sequence[int]] = None,
|
|
297
|
+
z: Optional[Sequence[int]] = None,
|
|
298
|
+
ps: Optional[Sequence[int]] = None,
|
|
299
|
+
reuse: bool = True,
|
|
300
|
+
noise_conf: Optional[Any] = None,
|
|
301
|
+
nmc: int = 1000,
|
|
302
|
+
status: Optional[Tensor] = None,
|
|
303
|
+
**kws: Any,
|
|
304
|
+
) -> Tensor:
|
|
305
|
+
"""
|
|
306
|
+
Shortcut for Pauli string expectation.
|
|
307
|
+
x, y, z list are for X, Y, Z positions
|
|
308
|
+
|
|
309
|
+
:Example:
|
|
310
|
+
|
|
311
|
+
>>> c = tc.Circuit(2)
|
|
312
|
+
>>> c.X(0)
|
|
313
|
+
>>> c.H(1)
|
|
314
|
+
>>> c.expectation_ps(x=[1], z=[0])
|
|
315
|
+
array(-0.99999994+0.j, dtype=complex64)
|
|
316
|
+
|
|
317
|
+
:param x: sites to apply X gate, defaults to None
|
|
318
|
+
:type x: Optional[Sequence[int]], optional
|
|
319
|
+
:param y: sites to apply Y gate, defaults to None
|
|
320
|
+
:type y: Optional[Sequence[int]], optional
|
|
321
|
+
:param z: sites to apply Z gate, defaults to None
|
|
322
|
+
:type z: Optional[Sequence[int]], optional
|
|
323
|
+
:param ps: or one can apply a ps structures instead of ``x``, ``y``, ``z``,
|
|
324
|
+
e.g. [0, 1, 3, 0, 2, 2] for X_1Z_2Y_4Y_5
|
|
325
|
+
defaults to None, ``ps`` can overwrite ``x``, ``y`` and ``z``
|
|
326
|
+
:type ps: Optional[Sequence[int]], optional
|
|
327
|
+
:param reuse: whether to cache and reuse the wavefunction, defaults to True
|
|
328
|
+
:type reuse: bool, optional
|
|
329
|
+
:param noise_conf: Noise Configuration, defaults to None
|
|
330
|
+
:type noise_conf: Optional[NoiseConf], optional
|
|
331
|
+
:param nmc: repetition time for Monte Carlo sampling for noisfy calculation, defaults to 1000
|
|
332
|
+
:type nmc: int, optional
|
|
333
|
+
:param status: external randomness given by tensor uniformly from [0, 1], defaults to None,
|
|
334
|
+
used for noisfy circuit sampling
|
|
335
|
+
:type status: Optional[Tensor], optional
|
|
336
|
+
:return: Expectation value
|
|
337
|
+
:rtype: Tensor
|
|
338
|
+
"""
|
|
339
|
+
return self.effective_circuit.expectation_ps(
|
|
340
|
+
x=x,
|
|
341
|
+
y=y,
|
|
342
|
+
z=z,
|
|
343
|
+
ps=ps,
|
|
344
|
+
reuse=reuse,
|
|
345
|
+
noise_conf=noise_conf,
|
|
346
|
+
nmc=nmc,
|
|
347
|
+
status=status,
|
|
348
|
+
**kws,
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
@partial(arg_alias, alias_dict={"format": ["format_"]})
|
|
352
|
+
def sample(
|
|
353
|
+
self,
|
|
354
|
+
batch: Optional[int] = None,
|
|
355
|
+
allow_state: bool = False,
|
|
356
|
+
readout_error: Optional[Sequence[Any]] = None,
|
|
357
|
+
format: Optional[str] = None,
|
|
358
|
+
random_generator: Optional[Any] = None,
|
|
359
|
+
status: Optional[Tensor] = None,
|
|
360
|
+
jittable: bool = True,
|
|
361
|
+
) -> Any:
|
|
362
|
+
r"""
|
|
363
|
+
Batched sampling from the circuit or final state.
|
|
364
|
+
|
|
365
|
+
:param batch: Number of samples. If `None`, returns a single draw.
|
|
366
|
+
:type batch: Optional[int]
|
|
367
|
+
:param allow_state: If `True`, sample from the final state (when memory allows). Prefer `True` for speed.
|
|
368
|
+
:type allow_state: bool
|
|
369
|
+
:param readout_error: Optional readout error model.
|
|
370
|
+
:type readout_error: Optional[Sequence[Any]]
|
|
371
|
+
:param format: Output format. See :py:meth:`tensorcircuit.quantum.measurement_results`.
|
|
372
|
+
:type format: Optional[str]
|
|
373
|
+
:param random_generator: random generator, defaults to None
|
|
374
|
+
:type random_generator: Optional[Any], optional
|
|
375
|
+
:param status: external randomness given by tensor uniformly from [0, 1],
|
|
376
|
+
if set, can overwrite random_generator, shape [batch] for `allow_state=True`
|
|
377
|
+
and shape [batch, nqudits] for `allow_state=False` using perfect sampling implementation
|
|
378
|
+
:type status: Optional[Tensor]
|
|
379
|
+
:param jittable: when converting to count, whether keep the full size. if false, may be conflict
|
|
380
|
+
external jit, if true, may fail for large scale system with actual limited count results
|
|
381
|
+
:type jittable: bool, defaults true
|
|
382
|
+
:return: List (if batch) of tuple (binary configuration tensor and corresponding probability)
|
|
383
|
+
if the format is None, and consistent with format when given
|
|
384
|
+
:rtype: Any
|
|
385
|
+
"""
|
|
386
|
+
return self.effective_circuit.sample(
|
|
387
|
+
batch=batch,
|
|
388
|
+
allow_state=allow_state,
|
|
389
|
+
readout_error=readout_error,
|
|
390
|
+
format=format,
|
|
391
|
+
random_generator=random_generator,
|
|
392
|
+
status=status,
|
|
393
|
+
jittable=jittable,
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
def __repr__(self) -> str:
|
|
397
|
+
s = f"AnalogCircuit(n={self.num_qubits}):\n"
|
|
398
|
+
s += "=" * 40 + "\n"
|
|
399
|
+
|
|
400
|
+
num_stages = len(self.analog_blocks) + 1
|
|
401
|
+
|
|
402
|
+
for i in range(num_stages):
|
|
403
|
+
# Print digital part
|
|
404
|
+
s += f"--- Digital Block {i} ---\n"
|
|
405
|
+
|
|
406
|
+
# Print analog part (if it exists)
|
|
407
|
+
if i < len(self.analog_blocks):
|
|
408
|
+
block = self.analog_blocks[i]
|
|
409
|
+
s += f"--- Analog Block {i} (T={block.time}) ---\n"
|
|
410
|
+
s += f" H(t) function: '{block.hamiltonian_func.__name__}'\n"
|
|
411
|
+
|
|
412
|
+
s += "=" * 40
|
|
413
|
+
return s
|
|
@@ -35,7 +35,7 @@ Symbol = Any # sympy.Symbol
|
|
|
35
35
|
|
|
36
36
|
def _resolve(symbol: Union[Symbol, Tensor], i: int = 0) -> Tensor:
|
|
37
37
|
"""
|
|
38
|
-
Make sure the layer is compatible with both multi-param and single param requirements
|
|
38
|
+
Make sure the layer is compatible with both multi-param and single param requirements
|
|
39
39
|
|
|
40
40
|
What could be the input: list/tuple of sympy.symbol, tf.tensor with 1D or 0D shape
|
|
41
41
|
"""
|
|
@@ -352,7 +352,7 @@ class NMF(Model): # type: ignore
|
|
|
352
352
|
spin_channel: int,
|
|
353
353
|
*dimensions: int,
|
|
354
354
|
_dtype: tf.DType = tf.float32,
|
|
355
|
-
probamp: Optional[tf.Tensor] = None
|
|
355
|
+
probamp: Optional[tf.Tensor] = None,
|
|
356
356
|
):
|
|
357
357
|
super().__init__()
|
|
358
358
|
self.w = self.add_weight(
|