fix: some small corrections
This commit is contained in:
1609
poetry.lock
generated
1609
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@ packages = [{ include = "qibotn", from = "src" }]
|
|||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = ">=3.11,<3.14"
|
python = ">=3.11,<3.14"
|
||||||
qibo = "^0.2.17"
|
qibo = { git="https://github.com/qiboteam/qibo", branch="expectation"}
|
||||||
quimb = { version = "^1.10.0", extras = ["tensor"] }
|
quimb = { version = "^1.10.0", extras = ["tensor"] }
|
||||||
cupy-cuda11x = { version = "^13.1.0", optional = true }
|
cupy-cuda11x = { version = "^13.1.0", optional = true }
|
||||||
cuquantum-python-cu11 = { version = "^24.1.0", optional = true }
|
cuquantum-python-cu11 = { version = "^24.1.0", optional = true }
|
||||||
|
|||||||
@@ -1,63 +1,55 @@
|
|||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from qibo.models import Circuit
|
|
||||||
from qibo.backends import NumpyBackend
|
|
||||||
from qibo.config import raise_error
|
|
||||||
from qibotn.backends.abstract import QibotnBackend
|
|
||||||
from qibotn.result import TensorNetworkResult
|
|
||||||
from qibo.gates.abstract import ParametrizedGate
|
|
||||||
|
|
||||||
import quimb as qu
|
import quimb as qu
|
||||||
import quimb.tensor as qtn
|
import quimb.tensor as qtn
|
||||||
|
from qibo.backends import NumpyBackend
|
||||||
|
from qibo.config import raise_error
|
||||||
|
from qibo.gates.abstract import ParametrizedGate
|
||||||
|
from qibo.models import Circuit
|
||||||
|
|
||||||
|
from qibotn.backends.abstract import QibotnBackend
|
||||||
|
from qibotn.result import TensorNetworkResult
|
||||||
|
|
||||||
GATE_MAP = {
|
GATE_MAP = {
|
||||||
"h": "H",
|
"h": "H",
|
||||||
"x": "X",
|
"x": "X",
|
||||||
"y": "Y",
|
"y": "Y",
|
||||||
"z": "Z",
|
"z": "Z",
|
||||||
"s": "S",
|
"s": "S",
|
||||||
"t": "T",
|
"t": "T",
|
||||||
|
"rx": "RX",
|
||||||
|
"ry": "RY",
|
||||||
|
"rz": "RZ",
|
||||||
|
"u3": "U3", # TODO: check
|
||||||
|
"cx": "CX",
|
||||||
|
"cnot": "CNOT",
|
||||||
|
"cy": "CY",
|
||||||
|
"cz": "CZ",
|
||||||
|
"iswap": "ISWAP",
|
||||||
|
"swap": "SWAP",
|
||||||
|
"ccx": "CCX",
|
||||||
|
"ccy": "CCY",
|
||||||
|
"ccz": "CCZ",
|
||||||
|
"toffoli": "TOFFOLI",
|
||||||
|
"cswap": "CSWAP",
|
||||||
|
"fredkin": "FREDKIN",
|
||||||
|
"fsim": "fsim",
|
||||||
|
"measure": "measure",
|
||||||
|
}
|
||||||
|
|
||||||
"rx": "RX",
|
|
||||||
"ry": "RY",
|
|
||||||
"rz": "RZ",
|
|
||||||
|
|
||||||
"u3": "U3", # TODO: check
|
|
||||||
|
|
||||||
"cx": "CX",
|
|
||||||
"cnot": "CNOT",
|
|
||||||
"cy": "CY",
|
|
||||||
"cz": "CZ",
|
|
||||||
|
|
||||||
"iswap": "ISWAP",
|
|
||||||
"swap": "SWAP",
|
|
||||||
|
|
||||||
"ccx": "CCX",
|
|
||||||
"ccy": "CCY",
|
|
||||||
"ccz": "CCZ",
|
|
||||||
|
|
||||||
"toffoli": "TOFFOLI",
|
|
||||||
"cswap": "CSWAP",
|
|
||||||
"fredkin": "FREDKIN",
|
|
||||||
|
|
||||||
"fsim": "fsim",
|
|
||||||
|
|
||||||
"measure": "measure"
|
|
||||||
}
|
|
||||||
|
|
||||||
class QuimbBackend(QibotnBackend, NumpyBackend):
|
class QuimbBackend(QibotnBackend, NumpyBackend):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, engine="numpy"):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.name = "qibotn"
|
self.name = "qibotn"
|
||||||
self.platform = "quimb"
|
self.platform = "quimb"
|
||||||
|
self.engine = engine
|
||||||
|
|
||||||
self.configure_tn_simulation()
|
self.configure_tn_simulation()
|
||||||
self.setup_backend_specifics()
|
self.setup_backend_specifics(quimb_backend=engine)
|
||||||
|
|
||||||
def configure_tn_simulation(
|
def configure_tn_simulation(
|
||||||
self,
|
self,
|
||||||
@@ -83,7 +75,9 @@ class QuimbBackend(QibotnBackend, NumpyBackend):
|
|||||||
self.max_bond_dimension = max_bond_dimension
|
self.max_bond_dimension = max_bond_dimension
|
||||||
self.n_most_frequent_states = n_most_frequent_states
|
self.n_most_frequent_states = n_most_frequent_states
|
||||||
|
|
||||||
def setup_backend_specifics(self, qimb_backend="numpy", contractions_optimizer="auto-hq"):
|
def setup_backend_specifics(
|
||||||
|
self, quimb_backend="numpy", contractions_optimizer="auto-hq"
|
||||||
|
):
|
||||||
"""Setup backend specifics.
|
"""Setup backend specifics.
|
||||||
Args:
|
Args:
|
||||||
qimb_backend: str
|
qimb_backend: str
|
||||||
@@ -91,19 +85,22 @@ class QuimbBackend(QibotnBackend, NumpyBackend):
|
|||||||
contractions_optimizer: str, optional
|
contractions_optimizer: str, optional
|
||||||
The contractions_optimizer to use for the quimb tensor network simulation.
|
The contractions_optimizer to use for the quimb tensor network simulation.
|
||||||
"""
|
"""
|
||||||
if qimb_backend == "jax":
|
if quimb_backend == "jax":
|
||||||
import jax.numpy as jnp
|
import jax.numpy as jnp
|
||||||
|
|
||||||
self.np = jnp
|
self.np = jnp
|
||||||
elif qimb_backend == "numpy":
|
elif quimb_backend == "numpy":
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
self.np = np
|
self.np = np
|
||||||
elif qimb_backend == "torch":
|
elif quimb_backend == "torch":
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
self.np = torch
|
self.np = torch
|
||||||
else:
|
else:
|
||||||
raise_error(ValueError, f"Unsupported quimb backend: {qimb_backend}")
|
raise_error(ValueError, f"Unsupported quimb backend: {quimb_backend}")
|
||||||
|
|
||||||
self.backend = qimb_backend
|
self.backend = quimb_backend
|
||||||
self.contractions_optimizer = contractions_optimizer
|
self.contractions_optimizer = contractions_optimizer
|
||||||
|
|
||||||
def execute_circuit(
|
def execute_circuit(
|
||||||
@@ -180,7 +177,9 @@ class QuimbBackend(QibotnBackend, NumpyBackend):
|
|||||||
measured_probabilities = None
|
measured_probabilities = None
|
||||||
|
|
||||||
statevector = (
|
statevector = (
|
||||||
circ_quimb.to_dense(backend=self.backend, optimize=self.contractions_optimizer)
|
circ_quimb.to_dense(
|
||||||
|
backend=self.backend, optimize=self.contractions_optimizer
|
||||||
|
)
|
||||||
if return_array
|
if return_array
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
@@ -193,7 +192,9 @@ class QuimbBackend(QibotnBackend, NumpyBackend):
|
|||||||
statevector=statevector,
|
statevector=statevector,
|
||||||
)
|
)
|
||||||
|
|
||||||
def expectation(self, circuit, operators_list, sites_list, coeffs_list):
|
def expectation_observable_symbolic_from_state(
|
||||||
|
self, circuit, operators_list, sites_list, coeffs_list, nqubits
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Compute the expectation value of a symbolic Hamiltonian on a quantum circuit using tensor network contraction.
|
Compute the expectation value of a symbolic Hamiltonian on a quantum circuit using tensor network contraction.
|
||||||
This method takes a Qibo circuit, converts it to a Quimb tensor network circuit, and evaluates the expectation value
|
This method takes a Qibo circuit, converts it to a Quimb tensor network circuit, and evaluates the expectation value
|
||||||
@@ -216,27 +217,30 @@ class QuimbBackend(QibotnBackend, NumpyBackend):
|
|||||||
float
|
float
|
||||||
The real part of the expectation value of the Hamiltonian on the given circuit state.
|
The real part of the expectation value of the Hamiltonian on the given circuit state.
|
||||||
"""
|
"""
|
||||||
quimb_circuit = self._qibo_circuit_to_quimb(circuit, quimb_circuit_type=qtn.Circuit)
|
quimb_circuit = self._qibo_circuit_to_quimb(
|
||||||
|
circuit, quimb_circuit_type=qtn.Circuit
|
||||||
|
)
|
||||||
|
|
||||||
expectation_value = 0.0
|
expectation_value = 0.0
|
||||||
for opstr, sitesstr, coeffstr in zip(operators_list, sites_list, coeffs_list):
|
for opstr, sites, coeff in zip(operators_list, sites_list, coeffs_list):
|
||||||
|
|
||||||
ops = self._string_to_quimb_operator(opstr)
|
ops = self._string_to_quimb_operator(opstr)
|
||||||
coeff = self._parse_coefficient(coeffstr)
|
coeff = coeff.real
|
||||||
sites = tuple(int(q) for q in sitesstr)
|
|
||||||
|
|
||||||
exp_values = quimb_circuit.local_expectation(
|
exp_values = quimb_circuit.local_expectation(
|
||||||
ops,
|
ops,
|
||||||
where=sites,
|
where=sites,
|
||||||
backend=self.backend,
|
backend=self.backend,
|
||||||
optimize=self.contractions_optimizer
|
optimize=self.contractions_optimizer,
|
||||||
)
|
)
|
||||||
|
|
||||||
expectation_value = expectation_value + coeff * exp_values
|
expectation_value = expectation_value + coeff * exp_values
|
||||||
|
|
||||||
return np.real(expectation_value)
|
return self.np.real(expectation_value)
|
||||||
|
|
||||||
def _qibo_circuit_to_quimb(self, qibo_circ, quimb_circuit_type=qtn.Circuit, **circuit_kwargs):
|
def _qibo_circuit_to_quimb(
|
||||||
|
self, qibo_circ, quimb_circuit_type=qtn.Circuit, **circuit_kwargs
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Convert a Qibo Circuit to a Quimb Circuit. Measurement gates are ignored. If are given gates not supported by Quimb, an error is raised.
|
Convert a Qibo Circuit to a Quimb Circuit. Measurement gates are ignored. If are given gates not supported by Quimb, an error is raised.
|
||||||
|
|
||||||
@@ -268,17 +272,11 @@ class QuimbBackend(QibotnBackend, NumpyBackend):
|
|||||||
params = getattr(gate, "parameters", ())
|
params = getattr(gate, "parameters", ())
|
||||||
qubits = getattr(gate, "qubits", ())
|
qubits = getattr(gate, "qubits", ())
|
||||||
|
|
||||||
is_parametrized = (
|
is_parametrized = isinstance(gate, ParametrizedGate) and getattr(
|
||||||
isinstance(gate, ParametrizedGate)
|
gate, "trainable", True
|
||||||
and getattr(gate, "trainable", True)
|
)
|
||||||
)
|
|
||||||
if is_parametrized:
|
if is_parametrized:
|
||||||
circ.apply_gate(
|
circ.apply_gate(qname, *params, *qubits, parametrized=is_parametrized)
|
||||||
qname,
|
|
||||||
*params,
|
|
||||||
*qubits,
|
|
||||||
parametrized=is_parametrized
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
circ.apply_gate(
|
circ.apply_gate(
|
||||||
qname,
|
qname,
|
||||||
@@ -287,28 +285,6 @@ class QuimbBackend(QibotnBackend, NumpyBackend):
|
|||||||
)
|
)
|
||||||
return circ
|
return circ
|
||||||
|
|
||||||
def _parse_coefficient(self, s):
|
|
||||||
"""Parse a coefficient from string to float, int, or complex.
|
|
||||||
Args:
|
|
||||||
s: str
|
|
||||||
The string representation of the coefficient.
|
|
||||||
Returns:
|
|
||||||
The coefficient as float, int, or complex.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return float(s)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
if s == "j":
|
|
||||||
return 1j
|
|
||||||
elif s == "-j":
|
|
||||||
return -1j
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
return complex(s)
|
|
||||||
except ValueError:
|
|
||||||
raise ValueError(f"Cannot parse coefficient: {s}")
|
|
||||||
|
|
||||||
def _string_to_quimb_operator(self, op_str):
|
def _string_to_quimb_operator(self, op_str):
|
||||||
"""
|
"""
|
||||||
Convert a Pauli string (e.g. 'xzy') to a Quimb operator using '&' chaining.
|
Convert a Pauli string (e.g. 'xzy') to a Quimb operator using '&' chaining.
|
||||||
|
|||||||
Reference in New Issue
Block a user