fix: some small corrections

This commit is contained in:
BrunoLiegiBastonLiegi
2025-09-23 16:27:29 +02:00
parent 1202091191
commit 2b5fca800c
3 changed files with 913 additions and 866 deletions

1609
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -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 }

View File

@@ -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,
@@ -71,7 +63,7 @@ class QuimbBackend(QibotnBackend, NumpyBackend):
Args: Args:
ansatz : str, optional ansatz : str, optional
The tensor network ansatz to use. Default is `None` and, in this case, a The tensor network ansatz to use. Default is `None` and, in this case, a
generic Circuit Quimb class is used. generic Circuit Quimb class is used.
max_bond_dimension : int, optional max_bond_dimension : int, optional
The maximum bond dimension for the MPS ansatz. Default is 10. The maximum bond dimension for the MPS ansatz. Default is 10.
@@ -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,14 +192,16 @@ 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
of a Hamiltonian specified by three lists of strings: operators, sites, and coefficients. of a Hamiltonian specified by three lists of strings: operators, sites, and coefficients.
The expectation value is computed by summing the contributions from each term in the Hamiltonian, where each term's The expectation value is computed by summing the contributions from each term in the Hamiltonian, where each term's
expectation is calculated using Quimb's `local_expectation` function. expectation is calculated using Quimb's `local_expectation` function.
Parameters Parameters
---------- ----------
circuit : qibo.models.Circuit circuit : qibo.models.Circuit
@@ -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)
def _qibo_circuit_to_quimb(self, qibo_circ, quimb_circuit_type=qtn.Circuit, **circuit_kwargs): return self.np.real(expectation_value)
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.
@@ -267,18 +271,12 @@ 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.