From b6b35d9a88da77acc9d8b8350b010c8d2e9e4545 Mon Sep 17 00:00:00 2001 From: Wong Zi Cheng <70616433+chmwzc@users.noreply.github.com> Date: Mon, 6 May 2024 09:49:17 +0000 Subject: [PATCH] Update tutorial 1 notebook For latest changes in Qibochem. Tutorial 2 currently in-progress --- ...on-to-quantum-computing-in-chemistry.ipynb | 136 +++++++++++------- 1 file changed, 81 insertions(+), 55 deletions(-) diff --git a/tutorials/1-introduction-to-quantum-computing-in-chemistry.ipynb b/tutorials/1-introduction-to-quantum-computing-in-chemistry.ipynb index ec8c76d..02b772e 100644 --- a/tutorials/1-introduction-to-quantum-computing-in-chemistry.ipynb +++ b/tutorials/1-introduction-to-quantum-computing-in-chemistry.ipynb @@ -49,8 +49,8 @@ " - Exact energy < Approximate energy\n", "- Define a loss function (electronic energy), and minimise it\n", " - Combines classical and quantum computing:\n", - " - Classical: Optimizers and molecular integrals\n", - " - Quantum: Represent the electronic wave function\n", + " - Classical computer: Optimizers and molecular integrals\n", + " - Quantum computer: Represent the electronic wave function\n", " - 2 main approaches (ansatz):\n", " - Chemistry-based:\n", " - Expensive, need to make cheaper\n", @@ -282,9 +282,7 @@ "metadata": {}, "outputs": [], "source": [ - "from qibo import models, gates\n", - "\n", - "# from qibochem.measurement.expectation import pauli_expectation_shots, circuit_expectation_shots" + "from qibo import Circuit, gates" ] }, { @@ -297,7 +295,7 @@ "# Example: the 0.1777 Z0 term for H2\n", "\n", "# First, we build a basic quantum circuit with 4 (= number of spin-orbitals, nso) qubits\n", - "circuit = models.Circuit(h2.nso)\n", + "circuit = Circuit(h2.nso)\n", "print(circuit.draw())" ] }, @@ -314,43 +312,69 @@ { "cell_type": "code", "execution_count": null, - "id": "97f05c26-235e-4774-ba79-cded9d789eed", + "id": "a6ed22f1-fb25-4a2d-86a8-83ecaef9bddd", "metadata": {}, "outputs": [], "source": [ - "from qibochem.measurement.expectation import expectation\n", + "circuit = Circuit(h2.nso)\n", + "circuit.add(gates.M(0)) # Add a measurement gate to measure the circuit result at the first qubit\n", + "\n", + "# Run the circuit\n", + "n_shots = 100\n", + "result = circuit(nshots=n_shots)\n", + "frequencies = result.frequencies(binary=True)\n", + "print(f\"Measurement result: {frequencies}\")" + ] + }, + { + "cell_type": "markdown", + "id": "93050394-488d-4044-b0cd-b9f5cd699736", + "metadata": {}, + "source": [ + "The measurement results are returned as a binary string. Since we only added one measurement gate, the result is a single digit: \"0\"\n", + "\n", + "---\n", "\n", - "from qibochem.driver.hamiltonian import symbolic_hamiltonian" + "The (statistical) results of running the circuit can then be used to calculate the expectation value of the Z0 term in the Hamiltonian." ] }, { "cell_type": "code", "execution_count": null, - "id": "62060716", + "id": "611e6f58-0355-4a8d-8e81-caff7b37de5e", "metadata": {}, "outputs": [], "source": [ - "circuit = models.Circuit(h2.nso)\n", - "# circuit = models.Circuit(1)\n", - "circuit.add(gates.M(0)) # Add a measurement gate to measure Z\n", - "print(circuit.draw())\n", - "\n", - "# Define the Z0 QubitOperator \n", - "z0_qubit_operator = openfermion.QubitOperator('Z0', 0.1777)\n", - "print(f\"\\nTerm: {z0_qubit_operator}\\n\")\n", + "from qibo.hamiltonians import SymbolicHamiltonian\n", + "from qibo.symbols import Z\n", "\n", - "# The pauli_expectation_shots(circuit, nshots) helper function runs and measures the output of circuit nshots times\n", - "# Since we're looking at the Z0 term, there's no need to apply any basis rotation gates before measurement\n", - "z0_expectation_value = expectation(circuit, symbolic_hamiltonian(z0_qubit_operator), from_samples=True, n_shots=100)\n", + "# Define the Z0 term of the Hamiltonian\n", + "z0 = SymbolicHamiltonian(0.1777*Z(0), nqubits=h2.nso)\n", + "z0_expectation_value = z0.expectation_from_samples(frequencies)\n", "print(f\"Expectation value: {z0_expectation_value}\")" ] }, { "cell_type": "markdown", - "id": "b7bbfb01", + "id": "ad375b17-fe2a-4d39-a620-598817dcd66e", "metadata": {}, "source": [ - "Note: pauli_expectation_shots just gives the measurement directly, which is just equal to 1.0 for a circuit with no gates applied" + "For convenience, Qibochem has its own `expectation_from_samples` function that is essentially the code in the the above cell" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9ca2b75-8eb7-4ebc-9c19-1aad095498ab", + "metadata": {}, + "outputs": [], + "source": [ + "from qibochem.measurement import expectation_from_samples\n", + "\n", + "# We need to re-define the circuit because it has already been executed\n", + "circuit = Circuit(h2.nso)\n", + "z0_expectation_value = expectation_from_samples(circuit, z0)\n", + "print(f\"Expectation value: {z0_expectation_value}\")" ] }, { @@ -376,27 +400,24 @@ "metadata": {}, "outputs": [], "source": [ - "# The same 0.1777 Z0 term, using the circuit state vector:\n", - "# Note: We need to broadcast the SymbolicHamiltonian using I (I as in identity) terms to match 4 qubits\n", - "# We use qibo.symbols here because openfermion.QubitOperator doesn't have I\n", - "from qibo.symbols import I, Z\n", - "from qibo.hamiltonians import SymbolicHamiltonian\n", - "\n", - "# The broadcasted Z_0 term using qibo.symbol\n", - "z0_qubit_operator_broadcasted = 0.1777*Z(0)*I(1)*I(2)*I(3)\n", - "# Convert it to a Qibo SymbolicHamiltonian\n", - "z0_symbolic = SymbolicHamiltonian(z0_qubit_operator_broadcasted)\n", - "\n", "# Build and run the simulated circuit to obtain its state vector\n", - "circuit = models.Circuit(h2.nso)\n", + "circuit = Circuit(h2.nso)\n", "circuit_result = circuit(nshots=1)\n", "circuit_state = circuit_result.state()\n", "# Calculate the expectation value using the state vector\n", - "z0_expectation_value = z0_symbolic.expectation(circuit_state)\n", + "z0_expectation_value = z0.expectation(circuit_state)\n", "\n", "print(f\"Expectation value: {z0_expectation_value}\")" ] }, + { + "cell_type": "markdown", + "id": "1c8f233d-9463-401e-9485-2e31e98fda67", + "metadata": {}, + "source": [ + "Again, Qibochem has its own `expectation` function for convenience" + ] + }, { "cell_type": "code", "execution_count": null, @@ -404,12 +425,10 @@ "metadata": {}, "outputs": [], "source": [ - "# Note: Helper function in Molecule class for expectation values\n", - "# TODO: Might change in next version of Qibo (0.1.12)\n", + "from qibochem.measurement import expectation\n", "\n", - "# Using the broadcasted SymbolicHamiltonian\n", - "circuit = models.Circuit(h2.nso)\n", - "print(f\"Expectation value: {expectation(circuit, z0_symbolic)}\")" + "circuit = Circuit(h2.nso)\n", + "print(f\"Expectation value: {expectation(circuit, z0)}\")" ] }, { @@ -417,7 +436,7 @@ "id": "7af310f7", "metadata": {}, "source": [ - "Going forward, we will just use the expectation value of the molecular Hamiltonian, obtained using the simulated state vector, for convenience." + "Going forward, we will just use the expectation value of the molecular Hamiltonian, obtained using the simulated state vector." ] }, { @@ -428,7 +447,7 @@ "outputs": [], "source": [ "# Example: Electronic energy of the vacuum state\n", - "circuit = models.Circuit(h2.nso)\n", + "circuit = Circuit(h2.nso)\n", "\n", "# Get the molecular Hamiltonian, and convert it to a SymbolicHamiltonian\n", "hamiltonian = h2.hamiltonian()\n", @@ -461,7 +480,7 @@ "metadata": {}, "outputs": [], "source": [ - "from qibochem.ansatz.hardware_efficient import hea" + "from qibochem.ansatz import he_circuit" ] }, { @@ -471,7 +490,8 @@ "metadata": {}, "outputs": [], "source": [ - "help(hea)" + "# The documentation can be consulted if one is unclear on how to use any specific functionality in Qibochem\n", + "help(he_circuit)" ] }, { @@ -485,15 +505,21 @@ }, "outputs": [], "source": [ - "# Get the list of quantum gates representing the ansatz\n", - "gate_list = hea(n_layers=1, n_qubits=h2.nso, coupling_gates='CNOT') # 1 layer is sufficient\n", - "\n", - "# Build a quantum circuit and add the gates from gate_list to it\n", - "circuit = models.Circuit(h2.nso)\n", - "circuit.add(gate for gate in gate_list)\n", + "# Build a quantum circuit representing a hardware-efficient ansatz\n", + "circuit = he_circuit(n_qubits=h2.nso, n_layers=1, coupling_gates='CNOT') # 1 layer is sufficient\n", "print(circuit.draw())" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "691f2d89-d8fc-4a4f-95d7-9a3da02cc6ce", + "metadata": {}, + "outputs": [], + "source": [ + "from qibo.models import VQE" + ] + }, { "cell_type": "code", "execution_count": null, @@ -509,8 +535,8 @@ "n_param_gates = len(circuit.get_parameters())\n", "\n", "# Create the VQE model\n", - "symbolic_hamiltonian = h2.hamiltonian()\n", - "vqe = models.VQE(circuit, symbolic_hamiltonian)\n", + "hamiltonian = h2.hamiltonian()\n", + "vqe = VQE(circuit, hamiltonian)\n", "\n", "# Optimize starting from a random guess for the variational parameters\n", "initial_parameters = np.random.uniform(0, 2*np.pi,\n", @@ -530,7 +556,7 @@ "\n", "print(f\"Classical HF energy: {h2.e_hf}\")\n", "# Exact groundstate energy of H2 as a reference\n", - "print(f\"{' ':>5}Exact solution: {symbolic_hamiltonian.eigenvalues()[0]}\")" + "print(f\"{' ':>5}Exact solution: {hamiltonian.eigenvalues()[0]}\")" ] }, { @@ -562,7 +588,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.11.9" } }, "nbformat": 4,