Skip to content

Update stateprep decomposition in tutorial_how_to_quantum_just_in_time_compile_vqe_catalyst demo #1325

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 22 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
372c23c
Merge branch 'master' into dev
actions-user Jan 21, 2025
2fd2d36
Merge branch 'master' into dev
actions-user Jan 23, 2025
f29da50
Merge branch 'master' into dev
actions-user Jan 23, 2025
55dfa98
Fix deprecated code usage in `tutorial_odegen.py` (#1311)
andrijapau Jan 28, 2025
0aaa21b
Merge branch 'master' into dev
actions-user Feb 3, 2025
68c6f08
Fix controlled qubit unitary usage (#1310)
JerryChen97 Feb 3, 2025
691322d
Fix `tutorial_pulse_programming101.py` to type cast `expval` (#1317)
andrijapau Feb 14, 2025
ea28de2
Merge branch 'master' into dev
actions-user Feb 14, 2025
1c30e26
Merge branch 'master' into dev
actions-user Feb 21, 2025
b354cb0
Merge branch 'master' into dev
actions-user Feb 24, 2025
fd6e502
Merge branch 'master' into dev
actions-user Feb 26, 2025
055b66c
Merge branch 'master' into dev
actions-user Feb 27, 2025
d3f3b93
Merge branch 'master' into dev
actions-user Mar 5, 2025
4cf80ef
Merge branch 'master' into dev
actions-user Mar 5, 2025
cc80022
Update `tutorial_noisy_circuits.py` to port `default.mixed` free of l…
JerryChen97 Mar 12, 2025
4b1b2a8
Remove compute_decomposition for the state pre instruction as it will…
mehrdad2m Mar 12, 2025
b8b8a6a
Merge branch 'dev' into update-statprep-decompose-demos
mehrdad2m Mar 12, 2025
d9cfa7b
Update demonstrations/tutorial_how_to_quantum_just_in_time_compile_vq…
mehrdad2m Mar 12, 2025
97e27db
Upgrade scipy (#1323)
JerryChen97 Mar 13, 2025
37e13ea
Merge branch 'dev' into update-statprep-decompose-demos
mehrdad2m Mar 17, 2025
88faa1b
Use `qml.lie_closure` and `qml.structure_constants` top level (#1329)
Qottmann Mar 17, 2025
55d79a6
Merge branch 'dev' into update-statprep-decompose-demos
mehrdad2m Mar 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
}
],
"dateOfPublication": "2022-10-31T00:00:00+00:00",
"dateOfLastModification": "2024-12-13T00:00:00+00:00",
"dateOfLastModification": "2025-01-23T00:00:00+00:00",
"categories": [
"Quantum Chemistry"
],
Expand Down
2 changes: 1 addition & 1 deletion demonstrations/tutorial_classically_boosted_vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ def hadamard_test(Uq, Ucl, component="real"):

qml.Hadamard(wires=[0])
qml.ControlledQubitUnitary(
Uq.conjugate().T @ Ucl, control_wires=[0], wires=wires[1:]
Uq.conjugate().T @ Ucl, wires=wires
)
qml.Hadamard(wires=[0])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def cost(params):
@qml.qjit
@qml.qnode(dev)
def cost(params):
qml.BasisState.compute_decomposition(hf, wires=range(qubits))
qml.BasisState(hf, wires=range(qubits))
qml.DoubleExcitation(params[0], wires=[0, 1, 2, 3])
qml.DoubleExcitation(params[1], wires=[0, 1, 4, 5])
return qml.expval(H)
Expand Down
2 changes: 1 addition & 1 deletion demonstrations/tutorial_kak_decomposition.metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
}
],
"dateOfPublication": "2024-11-25T00:00:00+00:00",
"dateOfLastModification": "2025-01-07T09:00:00+00:00",
"dateOfLastModification": "2025-03-17T09:00:00+00:00",
"categories": [
"Quantum Computing",
"Algorithms"
Expand Down
2 changes: 1 addition & 1 deletion demonstrations/tutorial_kak_decomposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ def check_cartan_decomposition(g, k, space_name):
"""Given an algebra g and an operator subspace k, verify that k is a subalgebra
and gives rise to a Cartan decomposition."""
# Check Lie closure of k
k_lie_closure = qml.pauli.dla.lie_closure(k)
k_lie_closure = qml.lie_closure(k)
k_is_closed = len(k_lie_closure) == len(k)
print(f"The Lie closure of k is as big as k itself: {k_is_closed}.")

Expand Down
2 changes: 1 addition & 1 deletion demonstrations/tutorial_liesim.metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
}
],
"dateOfPublication": "2024-06-07T00:00:00+00:00",
"dateOfLastModification": "2024-10-07T00:00:00+00:00",
"dateOfLastModification": "2025-03-17T00:00:00+00:00",
"categories": [
"Quantum Computing",
"Getting Started"
Expand Down
4 changes: 2 additions & 2 deletions demonstrations/tutorial_liesim.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
# work with PauliSentence instances for efficiency
generators = [op.pauli_rep for op in generators]

dla = qml.pauli.lie_closure(generators, pauli=True)
dla = qml.lie_closure(generators, pauli=True)
dim_g = len(dla)

##############################################################################
Expand Down Expand Up @@ -239,7 +239,7 @@
# the forward pass of the expectation value computation. For demonstration purposes,
# we choose a random subset of ``depth=10`` generators for gates from the DLA.

adjoint_repr = qml.pauli.structure_constants(dla)
adjoint_repr = qml.structure_constants(dla)

depth = 10
gate_choice = np.random.choice(dim_g, size=depth)
Expand Down
2 changes: 1 addition & 1 deletion demonstrations/tutorial_liesim_extension.metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
}
],
"dateOfPublication": "2024-06-18T00:00:00+00:00",
"dateOfLastModification": "2025-01-13T00:00:00+00:00",
"dateOfLastModification": "2025-03-17T00:00:00+00:00",
"categories": [
"Quantum Computing",
"Quantum Machine Learning"
Expand Down
2 changes: 1 addition & 1 deletion demonstrations/tutorial_liesim_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def TFIM(n):
generators += [Z(i) for i in range(n)]
generators = [op.pauli_rep for op in generators]

dla = qml.pauli.lie_closure(generators, pauli=True)
dla = qml.lie_closure(generators, pauli=True)
dim_dla = len(dla)
return generators, dla, dim_dla

Expand Down
2 changes: 1 addition & 1 deletion demonstrations/tutorial_noisy_circuits.metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
}
],
"dateOfPublication": "2021-02-22T00:00:00+00:00",
"dateOfLastModification": "2024-10-07T00:00:00+00:00",
"dateOfLastModification": "2024-12-18T00:00:00+00:00",
"categories": [
"Getting Started"
],
Expand Down
7 changes: 4 additions & 3 deletions demonstrations/tutorial_noisy_circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,13 @@ def bitflip_circuit(p):
qml.CNOT(wires=[0, 1])
qml.BitFlip(p, wires=0)
qml.BitFlip(p, wires=1)
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)), qml.state()


ps = [0.001, 0.01, 0.1, 0.2]
for p in ps:
print(f"QNode output for bit flip probability {p} is {bitflip_circuit(p):.4f}")
result = bitflip_circuit(p)
print(f"QNode output for bit flip probability {p} is {result[0]:.4f}")


######################################################################
Expand All @@ -158,7 +159,7 @@ def bitflip_circuit(p):
# mitigation and error correction are so important. We can use PennyLane to look under the hood and
# see the output state of the circuit for the largest noise parameter

print(f"Output state for bit flip probability {p} is \n{np.real(dev.state)}")
print(f"Output state for bit flip probability {p} is \n{result[1]}")

######################################################################
# Besides the bit flip channel, PennyLane supports several other noisy channels that are commonly
Expand Down
2 changes: 1 addition & 1 deletion demonstrations/tutorial_odegen.metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
}
],
"dateOfPublication": "2023-12-12T00:00:00+00:00",
"dateOfLastModification": "2024-10-07T00:00:00+00:00",
"dateOfLastModification": "2025-01-28T00:00:00+00:00",
"categories": [
"Optimization",
"Quantum Computing",
Expand Down
45 changes: 26 additions & 19 deletions demonstrations/tutorial_odegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@
that can be executed on hardware.


SPS & ODEgen
------------
SPS and ODEgen gradient rules
-----------------------------

Let us start by deriving both the SPS rule and ODEgen.
Let us start by deriving both the SPS and ODEgen rules.

We are interested in cost functions of the form

Expand Down Expand Up @@ -163,6 +163,7 @@
Let us define it in PennyLane and also import some libraries that we are going to need for this demo.

"""

import pennylane as qml
import numpy as np
import jax.numpy as jnp
Expand All @@ -181,21 +182,21 @@

##############################################################################
# We are going to consider a system of transmon qubits described by the Hamiltonian
#
#
# .. math:: H(\theta, t) = - \sum_i \frac{\omega_i}{2} Z_i + \sum_i \Omega_i(t) \sin(\nu_i t + \phi_i(t)) Y_i + \sum_{q, p \in \mathcal{C}} \frac{g_{qp}}{2} (X_i X_p + Y_i Y_p).
#
# The first term describes the single qubits with frequencies :math:`\omega_i.`
# The second term describes the driving with drive amplitudes :math:`\Omega_i,` drive frequencies :math:`\nu_i` and phases :math:`\phi_i.`
# You can check out our :doc:`recent demo on driving qubits on OQC's Lucy </demos/oqc_pulse>` if
#
# The first term describes the single qubits with frequencies :math:`\omega_i.`
# The second term describes the driving with drive amplitudes :math:`\Omega_i,` drive frequencies :math:`\nu_i` and phases :math:`\phi_i.`
# You can check out our :doc:`recent demo on driving qubits on OQC's Lucy </demos/oqc_pulse>` if
# you want to learn more about the details of controlling transmon qubits.
# The third term describes the coupling between neighboring qubits. We only have two qubits and a simple topology of
# The third term describes the coupling between neighboring qubits. We only have two qubits and a simple topology of
# :math:`\mathcal{C} = \{(0, 1)\},` hence only one coupling constant :math:`g_{01}.`
# The coupling is necessary to generate entanglement, which is achieved with cross-resonant driving in fixed-coupling
# The coupling is necessary to generate entanglement, which is achieved with cross-resonant driving in fixed-coupling
# transmon systems, as is the case here.
#
# We will use realistic parameters for the transmons, taken from the `coaxmon design paper <https://arxiv.org/abs/1905.05670>`_ [#Patterson]_
#
# We will use realistic parameters for the transmons, taken from the `coaxmon design paper <https://arxiv.org/abs/1905.05670>`_ [#Patterson]_
# (this is the blue-print for the transmon qubits in OQC's Lucy that you can :doc:`access on a pulse level in PennyLane </demos/oqc_pulse>`).
# In order to prepare the singlet ground state, we will perform a cross-resonance pulse, i.e. driving one qubit at its coupled neighbor's
# In order to prepare the singlet ground state, we will perform a cross-resonance pulse, i.e. driving one qubit at its coupled neighbor's
# frequency for entanglement generation (see [#Patterson]_ or [#Krantz]_) while simultaneously driving the other qubit on resonance.
# We choose a gate time of :math:`100 \text{ ns}.` We will use a piecewise constant function :func:`~pennylane.pulse.pwc` to parametrize both
# the amplitude :math:`\Omega_i(t)` and the phase :math:`\phi_i(t)` in time, with ``t_bins = 10`` time bins to allow for enough flexibility in the evolution.
Expand All @@ -221,7 +222,7 @@ def wrapped(p, t):
H_pulse += drive_field(T_CR, qubit_freq[0]) * qml.PauliY(wires[1]) # off-resonance driving of qubit 1

##############################################################################
# We can now define the cost function that computes the expectation value of
# We can now define the cost function that computes the expectation value of
# the Heisenberg Hamiltonian after evolving the state with the parametrized pulse Hamiltonian.
# We then define the two separate qnodes with ODEgen and SPS as their differentiation methods, respectively.

Expand All @@ -235,7 +236,14 @@ def qnode0(params):
value_and_grad_jax = jax.jit(jax.value_and_grad(qnode_jax))

num_split_times = 8
qnode_sps = qml.QNode(qnode0, dev, interface="jax", diff_method=qml.gradients.stoch_pulse_grad, use_broadcasting=True, num_split_times=num_split_times)
gradient_kwargs = {"use_broadcasting": True, "num_split_times": num_split_times}
qnode_sps = qml.QNode(
qnode0,
dev,
interface="jax",
diff_method=qml.gradients.stoch_pulse_grad,
gradient_kwargs=gradient_kwargs,
)
value_and_grad_sps = jax.value_and_grad(qnode_sps)

qnode_odegen = qml.QNode(qnode0, dev, interface="jax", diff_method=qml.gradients.pulse_odegen)
Expand Down Expand Up @@ -304,12 +312,12 @@ def partial_step(grad_circuit, opt_state, theta):


##############################################################################
# We see that with analytic gradients (ODEgen), we can reach the ground state energy within 100 epochs, whereas with SPS gradients we cannot find the path
# We see that with analytic gradients (ODEgen), we can reach the ground state energy within 100 epochs, whereas with SPS gradients we cannot find the path
# towards the minimum due to the stochasticity of the gradient estimates. Note that both optimizations start from the same (random) initial point.
# This picture solidifies when repeating this procedure for multiple runs from different random initializations, as was demonstrated in [#Kottmann]_.
#
# We also want to make sure that this is a fair comparison in terms of quantum resources. In the case of ODEgen, we maximally have :math:`\mathcal{R}_\text{ODEgen} = 2 (4^n - 1) = 30` expectation values.
# For SPS we have :math:`2 N_g N_s = 32` (due to :math:`N_g = 2` and :math:`N_s=8` time samples per gradient that we chose in ``num_split_times`` above). Thus, overall, we require fewer
# For SPS we have :math:`2 N_g N_s = 32` (due to :math:`N_g = 2` and :math:`N_s=8` time samples per gradient that we chose in ``num_split_times`` above). Thus, overall, we require fewer
# quantum resources for ODEgen gradients while achieving better performance.
#
# Conclusion
Expand All @@ -323,9 +331,8 @@ def partial_step(grad_circuit, opt_state, theta):
# Running VQE using ODEgen on hardware has recently been demonstrated in [#Kottmann]_ and you can directly find `the code here <https://github.com/XanaduAI/Analytic_Pulse_Gradients/tree/main/VQE_OQC>`_.



##############################################################################
#
#
# References
# ----------
#
Expand Down
2 changes: 1 addition & 1 deletion demonstrations/tutorial_pulse_programming101.metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
}
],
"dateOfPublication": "2023-03-08T00:00:00+00:00",
"dateOfLastModification": "2024-10-07T00:00:00+00:00",
"dateOfLastModification": "2025-02-14T00:00:00+00:00",
"categories": [
"Quantum Hardware",
"Quantum Computing"
Expand Down
14 changes: 10 additions & 4 deletions demonstrations/tutorial_pulse_programming101.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,15 +322,21 @@ def wrapped(p, t):

##############################################################################
# Now we define the ``qnode`` that computes the expectation value of the molecular Hamiltonian.
# We need to wrap the ``qnode`` in a function so that we can convert the expectation value to a real number.
# This will enable use to make use of gradient descent methods that require real-valued loss functions.

dev = qml.device("default.qubit", wires=range(n_wires))

@qml.qnode(dev, interface="jax")
def qnode(theta, t=duration):
qml.BasisState(jnp.array(data.tapered_hf_state), wires=H_obj.wires)
qml.evolve(H_pulse)(params=(*theta, *theta), t=t)
return qml.expval(H_obj)

@qml.qnode(dev)
def _qnode_inner(theta, t=duration):
qml.BasisState(jnp.array(data.tapered_hf_state), wires=H_obj.wires)
qml.evolve(H_pulse)(params=(*theta, *theta), t=t)
return qml.expval(H_obj)

expectation_value = _qnode_inner(theta, t) # Execute the qnode
return jnp.real(expectation_value) # Typecast to real number

value_and_grad = jax.jit(jax.value_and_grad(qnode))

Expand Down
2 changes: 1 addition & 1 deletion demonstrations/tutorial_testing_symmetry.metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
}
],
"dateOfPublication": "2023-01-24T00:00:00+00:00",
"dateOfLastModification": "2024-11-06T00:00:00+00:00",
"dateOfLastModification": "2025-01-23T00:00:00+00:00",
"categories": [
"Algorithms",
"Quantum Computing"
Expand Down
8 changes: 4 additions & 4 deletions demonstrations/tutorial_testing_symmetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,14 +308,14 @@ def prep_plus():

# Implement controlled symmetry operations on system
def CU_sys():
qml.ControlledQubitUnitary(c_mat @ c_mat, control_wires=[aux[0]], wires=system)
qml.ControlledQubitUnitary(c_mat, control_wires=[aux[1]], wires=system)
qml.ControlledQubitUnitary(c_mat @ c_mat, wires=[aux[0]] + list(system))
qml.ControlledQubitUnitary(c_mat, wires=[aux[1]] + list(system))


# Implement controlled symmetry operations on copy
def CU_cpy():
qml.ControlledQubitUnitary(c_mat @ c_mat, control_wires=[aux[0]], wires=copy)
qml.ControlledQubitUnitary(c_mat, control_wires=[aux[1]], wires=copy)
qml.ControlledQubitUnitary(c_mat @ c_mat, wires=[aux[0]] + list(copy))
qml.ControlledQubitUnitary(c_mat, wires=[aux[1]] + list(copy))

######################################################################
# Let’s combine everything and actually run our circuit!
Expand Down
2 changes: 1 addition & 1 deletion demonstrations_v2/tutorial_kak_decomposition/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ def check_cartan_decomposition(g, k, space_name):
"""Given an algebra g and an operator subspace k, verify that k is a subalgebra
and gives rise to a Cartan decomposition."""
# Check Lie closure of k
k_lie_closure = qml.pauli.dla.lie_closure(k)
k_lie_closure = qml.lie_closure(k)
k_is_closed = len(k_lie_closure) == len(k)
print(f"The Lie closure of k is as big as k itself: {k_is_closed}.")

Expand Down
2 changes: 1 addition & 1 deletion demonstrations_v2/tutorial_kak_decomposition/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
}
],
"dateOfPublication": "2024-11-25T00:00:00+00:00",
"dateOfLastModification": "2025-01-07T09:00:00+00:00",
"dateOfLastModification": "2025-03-17T09:00:00+00:00",
"categories": [
"Quantum Computing",
"Algorithms"
Expand Down
4 changes: 2 additions & 2 deletions demonstrations_v2/tutorial_liesim/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
# work with PauliSentence instances for efficiency
generators = [op.pauli_rep for op in generators]

dla = qml.pauli.lie_closure(generators, pauli=True)
dla = qml.lie_closure(generators, pauli=True)
dim_g = len(dla)

##############################################################################
Expand Down Expand Up @@ -239,7 +239,7 @@
# the forward pass of the expectation value computation. For demonstration purposes,
# we choose a random subset of ``depth=10`` generators for gates from the DLA.

adjoint_repr = qml.pauli.structure_constants(dla)
adjoint_repr = qml.structure_constants(dla)

depth = 10
gate_choice = np.random.choice(dim_g, size=depth)
Expand Down
2 changes: 1 addition & 1 deletion demonstrations_v2/tutorial_liesim/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
}
],
"dateOfPublication": "2024-06-07T00:00:00+00:00",
"dateOfLastModification": "2024-10-07T00:00:00+00:00",
"dateOfLastModification": "2025-03-17T00:00:00+00:00",
"categories": [
"Quantum Computing",
"Getting Started"
Expand Down
2 changes: 1 addition & 1 deletion demonstrations_v2/tutorial_liesim_extension/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def TFIM(n):
generators += [Z(i) for i in range(n)]
generators = [op.pauli_rep for op in generators]

dla = qml.pauli.lie_closure(generators, pauli=True)
dla = qml.lie_closure(generators, pauli=True)
dim_dla = len(dla)
return generators, dla, dim_dla

Expand Down
2 changes: 1 addition & 1 deletion demonstrations_v2/tutorial_liesim_extension/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
}
],
"dateOfPublication": "2024-06-18T00:00:00+00:00",
"dateOfLastModification": "2025-01-13T00:00:00+00:00",
"dateOfLastModification": "2025-03-17T00:00:00+00:00",
"categories": [
"Quantum Computing",
"Quantum Machine Learning"
Expand Down
Loading