Skip to content

Upload nonlinear amplitude transformation demo#1738

Open
drdren wants to merge 44 commits into
masterfrom
dren-nonlinear-QSVT
Open

Upload nonlinear amplitude transformation demo#1738
drdren wants to merge 44 commits into
masterfrom
dren-nonlinear-QSVT

Conversation

@drdren

@drdren drdren commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Before submitting

Please complete the following checklist when submitting a PR:

  • Ensure that your tutorial executes correctly, and conforms to the
    guidelines specified in the README.

  • Remember to do a grammar check of the content you include.

  • All tutorials conform to
    PEP8 standards.
    To auto format files, simply pip install black, and then
    run black -l 100 path/to/file.py.

When all the above are checked, delete everything above the dashed
line and fill in the pull request template.


Title:

Summary:

Relevant references:

Possible Drawbacks:

Related GitHub Issues:


If you are writing a demonstration, please answer these questions to facilitate the marketing process.

  • GOALS — Why are we working on this now?

    Eg. Promote a new PL feature or show a PL implementation of a recent paper.

  • AUDIENCE — Who is this for?

    Eg. Chemistry researchers, PL educators, beginners in quantum computing.

  • KEYWORDS — What words should be included in the marketing post?

  • Which of the following types of documentation is most similar to your file?
    (more details here)

  • Tutorial
  • [ X] Demo
  • How-to

@drdren drdren requested review from a team as code owners April 8, 2026 16:52
@github-actions

github-actions Bot commented Apr 8, 2026

Copy link
Copy Markdown

👋 Hey, looks like you've updated some demos!

🐘 Don't forget to update the dateOfLastModification in the associated metadata files so your changes are reflected in Glass Onion (search and recommendations).

Please hide this comment once the field(s) are updated. Thanks!

@drdren drdren left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to fix pip install

Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated

@drdren drdren left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove pip install

Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated

@drdren drdren left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add pip install pyqsp

Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated
Comment on lines +197 to +210
def MultiControlledZ(wires, control_values=None):
if control_values is None:
control_values = [0] * (len(wires) - 1)
qml.ctrl(qml.Z(wires=wires[-1]),
control=wires[:-1],
control_values=control_values)

# R_gate implements the reflection R used in the block construction.
def R(wires):
assert len(wires) % 2 == 1
n = len(wires)//2
qml.PauliX(wires=wires[0])
MultiControlledZ(wires=wires[1:n+1]+[wires[0]])
qml.PauliX(wires=wires[0])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look to qml.Reflection(wires)
That should replace all this code 😄

Comment on lines +440 to +445
def ProjCtrlPhaseShift(control_wires, target_wire, phi):
qml.MultiControlledX(wires=control_wires + target_wire,
control_values=[0] * len(control_wires))
qml.RZ(phi = 2 * phi, wires=target_wire)
qml.MultiControlledX(wires=control_wires + target_wire,
control_values=[0] * len(control_wires))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this qml.PCPhase?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think they are equivalent. I tested it numerically and they both apply different phase patterns.

Comment on lines +447 to +456
def generate_poly(deg, func, odd):
poly = PolyTaylorSeries().taylor_series(
func=func, degree=deg, max_scale=0.9,
chebyshev_basis=True, cheb_samples=2*deg)
pcoefs = poly.coef
if odd:
pcoefs[0::2] = 0
else:
pcoefs[1::2] = 0
return pcoefs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the way I get coefs given an arbitrary function
So no pyqsp needed here :)

from numpy.polynomial import Chebyshev as T
cheb_poly = T.interpolate(target_func, degree)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced the code.

@drdren drdren left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove pip install pyqsp

Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated
@github-actions

github-actions Bot commented Apr 17, 2026

Copy link
Copy Markdown

Your preview is ready 🎉!

You can view your changes here

Deployed at: 2026-06-25 19:37:55 UTC

@drdren drdren left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update figures

Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated
Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated
Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated
Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated
Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/metadata.json Outdated
Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/metadata.json Outdated
Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/metadata.json Outdated

@drdren drdren left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix figure formatting

Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated

@drdren drdren left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix bullet point list

@drdren drdren left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix bullet points

Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated
Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated

@drdren drdren left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix bullets

@drdren drdren left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are some suggestions!

Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated
# operator. In many quantum machine learning settings - especially amplitude encoding - the data isn’t
# stored in an operator at all. Instead, it lives directly in the amplitudes of a quantum state.
#
# To transform these amplitudes nonlinearly, we need a generalized approach. The Nonlinear Amplitude

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm quite confused at this point. The goal is clear: come up with a method to perform nonlinear transformations when a quantum computer only does linear operations.

Somehow, that became block encoding and QSVT, but those don't work?

What is the true goal and problem that the nonlinear amplitude transformation addresses? Does NLAT keep the data in the form of amplitude encoding?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QSVT transforms the singular values / eigenvalues of an operator A: A \mapsto P(A). It needs the data sitting in the spectrum of something you can block-encode. However, the data here is not in a spectrum so block encoding and QSVT don't work directly. NLAT makes it work by constructing an auxiliary unitary $U_\Psi$ that block-encodes the diagonal operator. Once the amplitudes are the diagonal entries of an operator, they are also its eigenvalues, i.e. its spectrum and now QSVT applies.

#
# So the amplitude vector :math:`{\psi_i}` appears as the first column of :math:`U`. The obstacle is
# that QSVT does not act on a column of a unitary; it acts on the spectrum of an operator accessed
# through a block encoding.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A similar confusion has occurred here. I could understand the definition of a block encoding. I could also understand that |\psi> = U |0> implies that the elements of \psi are in fact the left-most column vector of U.

"The obstacle that QSVT does not act ..." comes out of nowhere. I think some previewing of what we're trying to argue at the end of "Once :math:A is available in this form, QSVT can implement polynomial transformations of the encoded operator, informally :math:A \mapsto P(A), by acting on its spectrum." would be helpful.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The opening sentence of this section mentions "QSVT applies a polynomial to the singular values or eigenvalues of an operator." This sets the premise of why QSVT can't be directly applied to the amplitudes of a quantum state to perform nonlinear transformation.

#
# This is in sense equivalent to “encoding the first column into a diagonal,” but the key point is
# subtler: :math:`U` is not modified. Instead, an auxiliary unitary :math:`U_\Psi` is engineered so
# that the amplitudes :math:`\psi_i` appear as the diagonal entries of the encoded operator. In the

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So creating a block encoding that has diagonal entries has the spectrum we want but U_A does not have?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we take the data-encoding unitary U, which encodes the coefficients of |\psi>, and use it to build a larger unitary U_psi that block-encodes the diagonal operator \psi, on which we can then perform QSVT.

# spectrum (here, as the diagonal entries of :math:`\Psi`), a polynomial transform :math:`P(\Psi)`
# corresponds to applying :math:`\psi_k \mapsto P(\psi_k)` in parallel (up to postselection).
#
# Here, we build :math:`U_\Psi` explicitly for a small system (:math:`n=2`, so :math:`N=4`) to make

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know from context that n is the number of qubits and N = 2^n is the size of the Hilbert space but did you define those anywhere?

# As a broader perspective, the same “linear mixing + elementwise nonlinearity” motif underpins more
# advanced architectures. Recent work has explored the feasibility of quantum implementations of
# transformer-style inference under various access models and resource assumptions
# [`6 <https://arxiv.org/abs/2402.16714>`__]. The QMLP here should be viewed as a minimal instance of

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# [`6 <https://arxiv.org/abs/2402.16714>`__]. The QMLP here should be viewed as a minimal instance of
# [#qtransformer]_. The QMLP here should be viewed as a minimal instance of

Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated
Comment thread demonstrations_v2/tutorial_nonlinear_amplitude_transformation/demo.py Outdated
],
"seoDescription": "Learn about the nonlinear amplitude transformation using QSVT",
"doi": "",
"references": [

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the bibliography in demo.py is correct, then this references section should be changed accordingly.

# :width: 95%
# :align: center
#
# Figure 1: *A schematic of the nonlinear transformation transformation with QSVT*

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we have a great graphic explaining the process, maybe you want to point back to it throughout the demo?

drdren and others added 17 commits May 15, 2026 11:09
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
Co-authored-by: drdren <104710745+drdren@users.noreply.github.com>
######################################################################
# Diagonal block encoding of amplitudes
# -------------------------------------
#

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#
# Before diving in, reinforce your knowledge of the basics with our Intro to QSVT [and](https://pennylane.ai/demos/tutorial_intro_qsvt) QSVT in Practice [tutorials.](https://pennylane.ai/demos/tutorial_apply_qsvt)

# invocations of :math:`U` and :math:`U^\dagger`. For the purposes of this demo, we treat
# :math:`U_\Psi` as a primitive and focus on what it enables. The construction idea is intuitionally
# similar to building a quantum walk operator, and interested readers are encouraged to read original
# papers for details.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The construction idea is intuitionally similar to building a quantum walk operator, and interested readers are encouraged to read original papers for details [qubitization].

# corresponds to applying :math:`\psi_k \mapsto P(\psi_k)` in parallel (up to postselection).
#
# Here, we build :math:`U_\Psi` explicitly for a small system (:math:`n=2`, so :math:`N=4`) to make
# the construction tangible. The code below spells out the walk-style ingredients used in Guo et

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# the construction tangible. The code below spells out the walk-style ingredients used in Guo et
# the construction tangible. Here n is the number of qubits and :math:`N = 2^n` is the size of the Hilbert space. The code below spells out the walk-style ingredients used in Guo et

Comment on lines +161 to +164
# al. (2024): a reflection :math:`R`, controlled applications of the state-preparation unitary and its
# adjoint, and a pair of composite steps :math:`W` and :math:`G` that together produce the desired
# block structure. A phase toggle :math:`p \in \{0,1\}` switches between encoding the real part
# (:math:`p=0`) and the imaginary part (:math:`p=1`); here we focus on the real case.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# al. (2024): a reflection :math:`R`, controlled applications of the state-preparation unitary and its
# adjoint, and a pair of composite steps :math:`W` and :math:`G` that together produce the desired
# block structure. A phase toggle :math:`p \in \{0,1\}` switches between encoding the real part
# (:math:`p=0`) and the imaginary part (:math:`p=1`); here we focus on the real case.
# al. (2024):
# - a reflection :math:`R`
# - controlled applications of the state-preparation unitary and its adjoint
# - a pair of composite steps :math:`W` and :math:`G` that together produce the desired block structure.
# A phase toggle :math:`p \in \{0,1\}` switches between encoding the real part (:math:`p=0`) and the imaginary part (:math:`p=1`); here we focus on the real case.

Comment on lines +326 to +327
# Below we create a simple block‑encoding for :math:`n=2` and inspect its matrix to confirm that its
# diagonal corresponds to the input amplitudes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Below we create a simple block‑encoding for :math:`n=2` and inspect its matrix to confirm that its
# diagonal corresponds to the input amplitudes.
# Sanity check: after building the circuit, we inspect its matrix representation and look at the
# top-left :math:`N\times N` block. For a correct block-encoding, this block should behave like
# :math:`\Psi` (up to known normalization conventions), meaning its diagonal entries should match the
# input amplitudes :math:`\{\psi_k\}`. This is the smallest-scale verification that the circuit is
# implementing the intended “amplitudes :math:`\rightarrow` diagonal operator” transformation before
# we move on to applying QSVT polynomials.
# Below we create a simple block‑encoding for :math:`n=2` and inspect its matrix to confirm that its
# diagonal corresponds to the input amplitudes.

Comment on lines +447 to +456
def generate_poly(deg, func, odd):
poly = PolyTaylorSeries().taylor_series(
func=func, degree=deg, max_scale=0.9,
chebyshev_basis=True, cheb_samples=2*deg)
pcoefs = poly.coef
if odd:
pcoefs[0::2] = 0
else:
pcoefs[1::2] = 0
return pcoefs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced the code.

# Two ways to run the transformation
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# We compare two initializations:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# We compare two initializations:
# We compare two reference state initializations:

# So far, the demo has focused on the primitive itself: using diagonal block encodings and QSVT to
# implement an elementwise nonlinear map on amplitude-encoded data. Finally, we showcase the nonlinear
# transformation of complex amplitude (NTCA) method as a genuine non-linear activation layer within a
# trainable quantum model. We build a small quantum analogue of a two-layer MLP: two trainable linear

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already defined in the following sentence: two trainable linear layers (implemented as parameterized unitaries) separated by a tanh activation

labels = (pnp.array(ds.test['4']['labels'][:200])+1)/2

accuracy(best_weight, data, labels)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The goal of this section is not state-of-the-art accuracy. It is to show that the NLAT activation can be dropped into an end-to-end differentiable quantum model and trained. The modest accuracy is expected given the deliberately small model (2 data qubits, a degree-4 polynomial approximation of tanh, and only 100 optimization steps). Scaling any of these is the natural next step, but is outside the scope of this
minimal demonstration.

data = pnp.array(ds.test['4']['inputs'][:200])
labels = (pnp.array(ds.test['4']['labels'][:200])+1)/2

accuracy(best_weight, data, labels)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants