|
12 | 12 | # See the License for the specific language governing permissions and
|
13 | 13 | # limitations under the License.
|
14 | 14 | """
|
15 |
| -This module contains the default.tensor device to perform tensor network simulation of a quantum circuit using ``quimb``. |
| 15 | +This module contains the default.tensor device to perform tensor network simulations of quantum circuits using ``quimb``. |
16 | 16 | """
|
17 | 17 | import copy
|
18 | 18 | from dataclasses import replace
|
@@ -144,24 +144,104 @@ def accepted_observables(obs: qml.operation.Operator) -> bool:
|
144 | 144 | @simulator_tracking
|
145 | 145 | @single_tape_support
|
146 | 146 | class DefaultTensor(Device):
|
147 |
| - """A PennyLane device to perform tensor network operations on a quantum circuit using |
| 147 | + """A PennyLane device to perform tensor network simulations of quantum circuits using |
148 | 148 | `quimb <https://github.com/jcmgray/quimb/>`_.
|
149 | 149 |
|
| 150 | + This device is designed to simulate large-scale quantum circuits using tensor networks. For small circuits, other devices like ``default.qubit`` may be more suitable. |
| 151 | +
|
| 152 | + The backend uses the ``quimb`` library to perform the tensor network operations, and different methods can be used to simulate the quantum circuit. |
| 153 | + Currently, only the Matrix Product State (MPS) method is supported, based on ``quimb``'s ``CircuitMPS`` class. |
| 154 | +
|
| 155 | + This device does not currently support finite shots or differentiation. |
| 156 | + The currently supported measurement types are expectation values and variances. |
| 157 | +
|
150 | 158 | Args:
|
151 | 159 | wires (int, Iterable[Number, str]): Number of wires present on the device, or iterable that
|
152 | 160 | contains unique labels for the wires as numbers (i.e., ``[-1, 0, 2]``) or strings
|
153 | 161 | (``['aux_wire', 'q1', 'q2']``).
|
154 | 162 | method (str): Supported method. Currently, only ``"mps"`` is supported.
|
155 |
| - dtype (type): Datatype for the tensor representation. Must be one of ``np.complex64`` or ``np.complex128``. |
156 |
| - Default is ``np.complex128``. |
157 |
| - **kwargs: keyword arguments. The following options are currently supported: |
158 |
| -
|
159 |
| - ``max_bond_dim`` (int): Maximum bond dimension for the MPS simulator. |
160 |
| - It corresponds to the number of Schmidt coefficients retained at the end of the SVD algorithm when applying gates. Default is ``None``. |
161 |
| - ``cutoff`` (float): Truncation threshold for the Schmidt coefficients in a MPS simulator. Default is ``np.finfo(dtype).eps``. |
162 |
| - ``contract`` (str): The contraction method for applying gates. It can be either ``auto-mps`` or ``nonlocal``. |
163 |
| - ``nonlocal`` turns each gate into a MPO and applies it directly to the MPS, while ``auto-mps`` swaps nonlocal qubits in 2-qubit gates to be next |
164 |
| - to each other before applying the gate, then swaps them back. Default is ``auto-mps``. |
| 163 | + dtype (type): Data type for the tensor representation. Must be one of ``np.complex64`` or ``np.complex128``. |
| 164 | + **kwargs: keyword arguments for the device, passed to the ``quimb`` backend. |
| 165 | +
|
| 166 | + Keyword Args: |
| 167 | + max_bond_dim (int): Maximum bond dimension for the MPS method. |
| 168 | + It corresponds to the maximum number of Schmidt coefficients retained at the end of the SVD algorithm when applying gates. Default is ``None``. |
| 169 | + cutoff (float): Truncation threshold for the Schmidt coefficients in the MPS method. Default is the machine limit for the given tensor data type, |
| 170 | + retrieved with the ``numpy.finfo`` function. |
| 171 | + contract (str): The contraction method for applying gates in the MPS method. It can be either ``auto-mps`` or ``nonlocal``. |
| 172 | + ``nonlocal`` turns each gate into a Matrix Product Operator (MPO) and applies it directly to the MPS, |
| 173 | + while ``auto-mps`` swaps nonlocal qubits in 2-qubit gates to be next to each other before applying the gate, |
| 174 | + then swaps them back. Default is ``auto-mps``. |
| 175 | +
|
| 176 | + **Example:** |
| 177 | +
|
| 178 | + The following code shows how to create a simple short-depth quantum circuit with 100 qubits using the ``default.tensor`` device. |
| 179 | + Depending on the machine, the execution time for this circuit is around 0.3 seconds: |
| 180 | +
|
| 181 | + .. code-block:: python |
| 182 | +
|
| 183 | + import pennylane as qml |
| 184 | +
|
| 185 | + num_qubits = 100 |
| 186 | +
|
| 187 | + dev = qml.device("default.tensor", wires=num_qubits) |
| 188 | +
|
| 189 | + @qml.qnode(dev) |
| 190 | + def circuit(num_qubits): |
| 191 | + for qubit in range(0, num_qubits - 1): |
| 192 | + qml.CZ(wires=[qubit, qubit + 1]) |
| 193 | + qml.X(wires=[qubit]) |
| 194 | + qml.Z(wires=[qubit + 1]) |
| 195 | + return qml.expval(qml.Z(0)) |
| 196 | +
|
| 197 | + >>> circuit(num_qubits) |
| 198 | + tensor(-1., requires_grad=True) |
| 199 | +
|
| 200 | +
|
| 201 | + .. details:: |
| 202 | + :title: Usage Details |
| 203 | +
|
| 204 | + We can provide additional keyword arguments to the device to customize the simulation. These are passed to the ``quimb`` backend. |
| 205 | +
|
| 206 | + In the following example, we consider a slightly more complex circuit. We use the ``default.tensor`` device with the MPS method, |
| 207 | + setting the maximum bond dimension to 100 and the cutoff to 1e-16. We set ``"auto-mps"`` as the contraction technique to apply gates. |
| 208 | +
|
| 209 | + .. code-block:: python |
| 210 | +
|
| 211 | + import pennylane as qml |
| 212 | + import numpy as np |
| 213 | +
|
| 214 | + theta = 0.5 |
| 215 | + phi = 0.1 |
| 216 | + num_qubits = 50 |
| 217 | + device_kwargs = {"max_bond_dim": 100, "cutoff": 1e-16, "contract": "auto-mps"} |
| 218 | +
|
| 219 | + dev = qml.device("default.tensor", wires=num_qubits, **device_kwargs) |
| 220 | +
|
| 221 | + @qml.qnode(dev) |
| 222 | + def circuit(theta, phi, num_qubits): |
| 223 | + for qubit in range(num_qubits - 4): |
| 224 | + qml.X(wires=qubit) |
| 225 | + qml.RX(theta, wires=qubit + 1) |
| 226 | + qml.CNOT(wires=[qubit, qubit + 1]) |
| 227 | + qml.DoubleExcitation(phi, wires=[qubit, qubit + 1, qubit + 3, qubit + 4]) |
| 228 | + qml.CSWAP(wires=[qubit + 1, qubit + 3, qubit + 4]) |
| 229 | + qml.RY(theta, wires=qubit + 1) |
| 230 | + qml.Toffoli(wires=[qubit + 1, qubit + 3, qubit + 4]) |
| 231 | + return [ |
| 232 | + qml.expval(qml.Z(0)), |
| 233 | + qml.expval(qml.Hamiltonian([np.pi, np.e], [qml.Z(15) @ qml.Y(25), qml.Hadamard(40)])), |
| 234 | + qml.var(qml.Y(20)), |
| 235 | + ] |
| 236 | +
|
| 237 | + >>> circuit(theta, phi, num_qubits) |
| 238 | + [-0.9953099539219951, 0.0036631029671767208, 0.9999999876072984] |
| 239 | +
|
| 240 | + After the first execution, the time to run this circuit for 50 qubits is around 0.5 seconds depending on the machine. |
| 241 | + Increasing the number of qubits to 500 brings the execution time to approximately 15 seconds, and for 1000 qubits to around 50 seconds. |
| 242 | +
|
| 243 | + The time complexity and the accuracy of the results also depend on the chosen keyword arguments for the device, such as the maximum bond dimension. |
| 244 | + The specific structure of the circuit significantly affects how the time complexity and accuracy of the simulation scale with these parameters. |
165 | 245 | """
|
166 | 246 |
|
167 | 247 | # pylint: disable=too-many-instance-attributes
|
@@ -258,7 +338,11 @@ def dtype(self):
|
258 | 338 | return self._dtype
|
259 | 339 |
|
260 | 340 | def _reset_state(self) -> None:
|
261 |
| - """Reset the MPS.""" |
| 341 | + """ |
| 342 | + Reset the MPS. |
| 343 | +
|
| 344 | + This method modifies the tensor state of the device. |
| 345 | + """ |
262 | 346 | self._circuitMPS = qtn.CircuitMPS(psi0=self._initial_mps())
|
263 | 347 |
|
264 | 348 | def _initial_mps(self) -> "qtn.MatrixProductState":
|
@@ -382,7 +466,7 @@ def simulate(self, circuit: QuantumScript) -> Result:
|
382 | 466 | def _apply_operation(self, op: qml.operation.Operator) -> None:
|
383 | 467 | """Apply a single operator to the circuit, keeping the state always in a MPS form.
|
384 | 468 |
|
385 |
| - Internally it uses `quimb`'s `apply_gate` method. |
| 469 | + Internally it uses `quimb`'s `apply_gate` method. This method modifies the tensor state of the device. |
386 | 470 |
|
387 | 471 | Args:
|
388 | 472 | op (Operator): The operation to apply.
|
|
0 commit comments