From d4ab3eba52950f09111038d5388a045c9f8d55b4 Mon Sep 17 00:00:00 2001 From: mattia-robbiano Date: Thu, 13 Nov 2025 10:45:32 +0200 Subject: [PATCH] minor improvements to examples, documentation, internally used variable names --- examples/quimb_intro/quimb_introduction.ipynb | 55 ++++++++++++------- src/qibotn/backends/quimb.py | 37 ++++++++----- 2 files changed, 59 insertions(+), 33 deletions(-) diff --git a/examples/quimb_intro/quimb_introduction.ipynb b/examples/quimb_intro/quimb_introduction.ipynb index 1e043ac..5eaacc4 100644 --- a/examples/quimb_intro/quimb_introduction.ipynb +++ b/examples/quimb_intro/quimb_introduction.ipynb @@ -28,7 +28,7 @@ }, { "cell_type": "markdown", - "id": "a009a5e0-cfd4-4a49-9f7c-e82f252c6147", + "id": "0c5a8939", "metadata": {}, "source": [ "#### Some hyper parameters" @@ -36,24 +36,7 @@ }, { "cell_type": "code", - "execution_count": 2, - "id": "b0a1da82", - "metadata": {}, - "outputs": [], - "source": [ - "import cotengra as ctg\n", - "ctg_opt = ctg.ReusableHyperOptimizer(\n", - " max_time=10,\n", - " minimize='combo',\n", - " slicing_opts=None,\n", - " parallel=True,\n", - " progbar=True\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "64162116-1555-4a68-811c-01593739d622", "metadata": {}, "outputs": [], @@ -67,7 +50,39 @@ "# set numpy random seed\n", "np.random.seed(42)\n", "\n", - "quimb_backend.setup_backend_specifics(quimb_backend=\"jax\")" + "quimb_backend.setup_backend_specifics(quimb_backend=\"jax\", contractions_optimizer='auto-hq')" + ] + }, + { + "cell_type": "markdown", + "id": "926cfea5", + "metadata": {}, + "source": [ + "Quimb accepts different methods for optimizing the way it does contractions, that we pass through \"contractions_optimizer\". \n", + "We could also define our own cotengra contraction optimizer! \n", + "\n", + "cotengra is a Python library designed for **optimising contraction trees** and performing efficient contractions of large tensor‐networks.\n", + "You can find it here: [https://github.com/jcmgray/cotengra](https://github.com/jcmgray/cotengra)\n", + "\n", + "For the sake of this tutorial however the default \"auto-hq\" will be fine :) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0a1da82", + "metadata": {}, + "outputs": [], + "source": [ + "import cotengra as ctg\n", + "ctg_opt = ctg.ReusableHyperOptimizer(\n", + " max_time=10,\n", + " minimize='combo',\n", + " slicing_opts=None,\n", + " parallel=True,\n", + " progbar=True\n", + ")\n", + "# quimb_backend.setup_backend_specifics(quimb_backend=\"jax\", contractions_optimizer='ctg_opt')" ] }, { diff --git a/src/qibotn/backends/quimb.py b/src/qibotn/backends/quimb.py index d72361f..21d54ec 100644 --- a/src/qibotn/backends/quimb.py +++ b/src/qibotn/backends/quimb.py @@ -20,7 +20,7 @@ GATE_MAP = { "rx": "RX", "ry": "RY", "rz": "RZ", - "u3": "U3", # TODO: check + "u3": "U3", "cx": "CX", "cnot": "CNOT", "cy": "CY", @@ -210,7 +210,10 @@ def expectation_observable_symbolic( 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. 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. + Each operator string must act on all different qubits, i.e., for each term, the corresponding sites tuple must contain unique qubit indices. + Example: operators_list = ['xyz', 'xyz'], sites_list = [(1,2,3), (1,2,3)], coeffs_list = [1, 2] + Parameters ---------- @@ -218,15 +221,23 @@ def expectation_observable_symbolic( The quantum circuit to evaluate, provided as a Qibo circuit object. operators_list : list of str List of operator strings representing the symbolic Hamiltonian terms. - sites_list : list of str - List of strings, each specifying the qubits (sites) the corresponding operator acts on. - coeffs_list : list of str - List of strings representing the coefficients for each Hamiltonian term. + sites_list : list of tuple of int + Tuples each specifying the qubits (sites) the corresponding operator acts on. + coeffs_list : list of real/complex + The coefficients for each Hamiltonian term. Returns ------- float The real part of the expectation value of the Hamiltonian on the given circuit state. """ + # Validate that no term acts multiple times on the same qubit (no repeated indices in a sites tuple) + for sites in sites_list: + if len(sites) != len(set(sites)): + raise_error( + ValueError, + f"Invalid Hamiltonian term sites {sites}: repeated qubit indices are not allowed " + "within a single term (e.g. (0,0,0) is invalid).", + ) quimb_circuit = self._qibo_circuit_to_quimb( circuit, quimb_circuit_type=self.circuit_ansatz, @@ -276,12 +287,12 @@ def _qibo_circuit_to_quimb( circ = quimb_circuit_type(nqubits, **circuit_kwargs) for gate in qibo_circ.queue: - gname = getattr(gate, "name", None) - qname = GATE_MAP.get(gname, None) - if qname == "measure": + gate_name = getattr(gate, "name", None) + quimb_gate_name = GATE_MAP.get(gate_name, None) + if quimb_gate_name == "measure": continue - if qname is None: - raise_error(ValueError, f"Gate {gname} not supported in Quimb backend.") + if quimb_gate_name is None: + raise_error(ValueError, f"Gate {gate_name} not supported in Quimb backend.") params = getattr(gate, "parameters", ()) qubits = getattr(gate, "qubits", ()) @@ -290,10 +301,10 @@ def _qibo_circuit_to_quimb( gate, "trainable", True ) if is_parametrized: - circ.apply_gate(qname, *params, *qubits, parametrized=is_parametrized) + circ.apply_gate(quimb_gate_name, *params, *qubits, parametrized=is_parametrized) else: circ.apply_gate( - qname, + quimb_gate_name, *params, *qubits, )