Repository: PennyLaneAI/qml
Pull request #1324: Add demo on period finding
Author: login: mariaschuld
Created: 2025-03-10T18:57:20Z | Merged: 2025-04-16T15:55:33Z

**Title:**

Period finding -- a problem at the heard of quantum computing

We could use a less bulky title

**Summary:**

Introduces hidden subgroup problems, using a simple example.

* GOALS — Why are we working on this now?

Basis for the theory we're crafting in the QML team. Good basic quantum computing content to share.

* AUDIENCE — Who is this for?

Quantum computing researchers in general

* KEYWORDS — What words should be included in the marketing post?

Hidden subgroup problem, period finding, Shor's algorithm, Abelian groups, Quantum Fourier Transform

* Which of the following types of documentation is most similar to your file? 
(more details [here](https://www.notion.so/xanaduai/Different-kinds-of-documentation-69200645fe59442991c71f9e7d8a77f8))
    
- [ ] Tutorial
- [ X] Demo
- [ ] How-to

[sc-81652]


Diff:
diff --git a/_static/demo_thumbnails/large_demo_thumbnails/thumbnail_large_period_finding.png b/_static/demo_thumbnails/large_demo_thumbnails/thumbnail_large_period_finding.png
new file mode 100644
index 0000000000..5197ec919b
Binary files /dev/null and b/_static/demo_thumbnails/large_demo_thumbnails/thumbnail_large_period_finding.png differ
diff --git a/_static/demo_thumbnails/opengraph_demo_thumbnails/OGthumbnail_period_finding.png b/_static/demo_thumbnails/opengraph_demo_thumbnails/OGthumbnail_period_finding.png
new file mode 100644
index 0000000000..ccefceff37
Binary files /dev/null and b/_static/demo_thumbnails/opengraph_demo_thumbnails/OGthumbnail_period_finding.png differ
diff --git a/_static/demo_thumbnails/regular_demo_thumbnails/thumbnail_period_finding.png b/_static/demo_thumbnails/regular_demo_thumbnails/thumbnail_period_finding.png
new file mode 100644
index 0000000000..7cd92f76ca
Binary files /dev/null and b/_static/demo_thumbnails/regular_demo_thumbnails/thumbnail_period_finding.png differ
diff --git a/_static/demonstration_assets/period_finding/aaronson_fig6.png b/_static/demonstration_assets/period_finding/aaronson_fig6.png
new file mode 100644
index 0000000000..9f857b4678
Binary files /dev/null and b/_static/demonstration_assets/period_finding/aaronson_fig6.png differ
diff --git a/_static/demonstration_assets/period_finding/periodic_function.png b/_static/demonstration_assets/period_finding/periodic_function.png
new file mode 100644
index 0000000000..6b980cdd51
Binary files /dev/null and b/_static/demonstration_assets/period_finding/periodic_function.png differ
diff --git a/_static/demonstration_assets/period_finding/periodic_function_groups.png b/_static/demonstration_assets/period_finding/periodic_function_groups.png
new file mode 100644
index 0000000000..ac261f3335
Binary files /dev/null and b/_static/demonstration_assets/period_finding/periodic_function_groups.png differ
diff --git a/demonstrations/tutorial_period_finding.metadata.json b/demonstrations/tutorial_period_finding.metadata.json
new file mode 100644
index 0000000000..76adf7b1b2
--- /dev/null
+++ b/demonstrations/tutorial_period_finding.metadata.json
@@ -0,0 +1,61 @@
+{
+    "title": "Period finding: A problem at the heart of quantum computing",
+    "authors": [
+        {
+            "username": "mariaschuld"
+        }
+    ],
+    "dateOfPublication": "2025-04-16T10:00:00+00:00",
+    "dateOfLastModification": "2025-04-16T10:00:00+00:00",
+    "categories": [
+        "Algorithms"
+    ],
+    "tags": [],
+    "previewImages": [
+        {
+            "type": "thumbnail",
+            "uri": "/_static/demo_thumbnails/regular_demo_thumbnails/thumbnail_period_finding.png"
+        },
+        {
+            "type": "large_thumbnail",
+            "uri": "/_static/demo_thumbnails/large_demo_thumbnails/thumbnail_large_period_finding.png"
+        }
+    ],
+    "seoDescription": "Find out how quantum computers exploit group structure and the Fourier transform to uncover hidden patterns in functions.",
+    "doi": "",
+    "references": [
+        {
+            "id": "Aaronson2022",
+            "type": "preprint",
+            "title": "How Much Structure Is Needed for Huge Quantum Speedups?",
+            "authors": "Scott Aaronson",
+            "year": "2022",
+            "journal": "arXiv",
+            "url": "https://arxiv.org/pdf/2209.06930"
+        },
+        {
+            "id": "Childs2010",
+            "type": "article",
+            "title": "Quantum algorithms for algebraic problems",
+            "authors": "Andrew M. Childs, Wim van Dam",
+            "year": "1997",
+            "journal": "Reviews of Modern Physics 82, 1",
+            "doi": "10.1103/RevModPhys.82.1",
+            "url": "https://doi.org/10.1103/RevModPhys.82.1"
+        }
+    ],
+    "basedOnPapers": [],
+    "referencedByPapers": [],
+    "relatedContent": [
+        {
+            "type": "demonstration",
+            "id": "tutorial_qft",
+            "weight": 1.0
+        },
+        {
+            "type": "demonstration",
+            "id": "tutorial_qft_arithmetics",
+            "weight": 1.0
+        }
+    ]
+}
diff --git a/demonstrations/tutorial_period_finding.py b/demonstrations/tutorial_period_finding.py
new file mode 100644
index 0000000000..d2f7030fbe
--- /dev/null
+++ b/demonstrations/tutorial_period_finding.py
@@ -0,0 +1,387 @@
+r"""
+Period finding: A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+    Figure 1. Aaronson's 'hierarchy of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of an integer valued function—something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# The period finding problem
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 90%
+#    :target: javascript:void(0);
+#    :figwidth: 80%
+#    
+#    Figure 2. Example of a discrete periodic function f(x) over the integers x = 0,...,11. 
+#    The function only takes the same value when moving exactly 4 integers on the x-axis.
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder, this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)``. Your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. 
+#
+# .. admonition:: Abelian group
+#     :class: note
+#
+#     A group is a set of elements that has:
+#       1. an operation that maps two elements a and b of the set into a third element of the set, for example c = a + b,
+#       2. an "identity element" e such that e + a = a for any element a, and
+#       3. an inverse -a for every element a, such that a + (-a) = e.
+#
+#     A group is called "Abelian" if a + b = b + a for all a and b, otherwise it is called non-Abelian. 
+#     In general, Abelian groups are simpler to work with than non-Abelian groups.
+#
+#     With this definition we can easily see that the group of integers {0,1,...,11} with addition modulo 12 forms an Abelian group: 
+#     there is an addition operation such that a + b = b + a, the element 0 is the identity element since 0 + a = a for 
+#     all a, and the inverse of element a modulo 12 is 12 - a.
+#
+# The values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#    :target: javascript:void(0);
+#    
+#    Figure 3. Periodic function from above, from the perspective of group theory.
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding—and the algorithm we will have a look at here—can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-processing:
+# The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The number of periods is then the biggest number that divides all the measurement outcomes, 
+# and the period is the number of integers divided by the number of periods. 
+# In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the largest common divisor as 3, which is the number of periods fitting
+# into 12. From there we can recover the number of periods 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation of period finding in PennyLane
+# ----------------
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and—theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit—we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+fig = plt.figure()
+ax = fig.add_axes((0.1, 0.2, 0.8, 0.7))
+ax.scatter(x, y)
+ax.set_title("Periodic function")
+ax.set_ylabel("f(x)")
+ax.set_xlabel("x")
+fig.text(0.5, 0.05, "Figure 4. Periodic function f(x)", horizontalalignment='center')
+plt.show()
+
+#####################################################################
+# 
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 2` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application, this 
+# would be a quantum circuit defined by a sequence of gates.
+
+
+def Oracle(f):
+    """
+    Defines the unitary that implements a function f:{0,..,15} -> {0,..,3}.
+
+    Args:
+      f (func): function to implement
+
+    Returns:
+      ndarray representing the unitary
+    """
+
+    U = np.zeros((2**7, 2**7))
+
+    for x in range(2**4):
+        for f_x in range(2**3):
+            # we know that the initial state has only support on basis states
+            # of the form |x>|0>, and therefore write all necessary information
+            # into the entries of the unitary acting on those states
+            if f_x == 0:
+                i = to_int(to_binary(x, 4) + "0" * 3)
+                j = to_int(to_binary(x, 4) + to_binary(f(x), 3))
+                U[i, j] = 1
+                U[j, i] = 1
+            else:
+                # in all other cases we use trivial entries
+                i = to_int(to_binary(x, 4) + to_binary(f_x, 3))
+                U[i, i] = 1
+
+    # check that this is a unitary
+    assert np.allclose(U @ np.linalg.inv(U), np.eye(2**7))
+
+    return qml.QubitUnitary(U, wires=range(7))
+
+
+#####################################################################
+# Now we're ready to go. Let's implement the famous period finding algorithm. It consists of a
+# quantum routine and a classical post-processing step. As mentioned, the quantum part is simple: prepare the
+# desired initial state :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum Fourier transform
+# onto the :math:`|x\rangle`-register, and measure in the computational basis. Effectively, this
+# measures in the "Fourier basis", which is where all the magic happens.
+#
+# We only have to get two unique samples, from which we can compute the period of the function. For this 
+# reason we define a device with 2 shots. We also add some snapshots to the circuit that we will look at later.
+
+
+dev = qml.device("default.qubit", wires=7, shots=2)
+
+
+@qml.qnode(dev)
+def circuit():
+    """Circuit to implement the period finding algorithm."""
+
+    for i in range(4):
+        qml.Hadamard(wires=i)
+
+    qml.Snapshot("initial_state")
+
+    Oracle(f)
+
+    qml.Snapshot("loaded_function")
+
+    qml.QFT(wires=range(4))
+
+    qml.Snapshot("fourier_spectrum")
+
+    return qml.sample(wires=range(4))
+
+
+# take two samples from the circuit
+samples = circuit()
+
+#####################################################################
+# The classical post-processing is relatively simple for period finding (whereas 
+# for general hidden subgroup problems it requires some less trivial algebra). 
+# If you are curious about the details, you'll find them in
+# Childs & van Dam (2013) [#Childs2010]_, Section IVa.
+
+from fractions import Fraction
+from math import lcm
+
+# convert the bistrings to integers
+sample1_int = to_int("".join(str(s) for s in samples[0]))
+sample2_int = to_int("".join(str(s) for s in samples[1]))
+
+# get the denominator of the fraction representation
+denominator1 = Fraction(sample1_int / (2**4)).denominator
+denominator2 = Fraction(sample2_int / (2**4)).denominator
+
+# get the least common multiple
+result = lcm(denominator1, denominator2)
+
+print(f"Hidden period: {result}")
+
+#####################################################################
+# Yay, we've found the hidden period! Now, of course this is only impressive if we increase the 
+# number of qubits. We can find the hidden period of a function defined on :math:`2^n` values in time
+# :math:`O(n \mathrm{log}(n))`.
+
+
+#####################################################################
+# A peep at the states involved
+# -----------------------------
+#
+# Somehow the quantum Fourier transform exposed the information we needed. Let's have a closer
+# look at the states that were prepared by making use of the snapshots we recorded during the
+# circuit simulation.
+
+dev = qml.device("default.qubit", wires=7, shots=1)
+qnode = qml.QNode(circuit, dev)
+intermediate_states = qml.snapshots(circuit)()
+
+#####################################################################
+# We can plot them as discrete functions, where the size of a point indicates the absolute value
+# of the amplitude.
+
+import matplotlib.patches as mpatches
+
+x = [i for i in range(2**4) for _ in range(2**3)]
+y = list(range(2**3)) * (2**4)
+
+states_to_plot = ["initial_state", "loaded_function", "fourier_spectrum"]
+
+fig, axes = plt.subplots(3, 1, sharex=True, figsize=(7, 6))
+
+for state, ax in zip(states_to_plot, axes):
+
+    real = np.real(intermediate_states[state])
+    imag = np.imag(intermediate_states[state])
+
+    # scale the points by the absolute value of the amplitude
+    sizes_real = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_real.append(200 * np.abs(real[idx]))
+
+    sizes_imag = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_imag.append(200 * np.abs(imag[idx]))
+
+    ax.scatter(np.array(x), y, s=sizes_real, c="green", alpha=0.5)
+    ax.scatter(np.array(x), y, s=sizes_imag, c="pink", alpha=0.5)
+    ax.set_title(state)
+    ax.set_ylabel("f(x)")
+
+plt.xlabel("x")
+green = mpatches.Patch(color="green", label="abs(real part)")
+pink = mpatches.Patch(color="pink", label="abs(imaginary part)")
+plt.legend(handles=[green, pink])
+fig.suptitle("Figure 5. Representing the Fourier spectrum", horizontalalignment='center')
+fig.tight_layout()
+plt.show()
+
+#####################################################################
+# 
+# First of all, we see that the oracle really prepared a quantum state that we can interpret as 
+# our periodic function ``f``.
+#
+# Furthermore, the state representing the Fourier spectrum looks actually quite interesting. But
+# the important feature of this state is also clearly visible: Amplitudes concentrate in :math:`x`
+# values that are multiples of 2. And 2 was exactly the amount of periods of 8 that fit into 16.
+
+#####################################################################
+# What is the "Abelian structure" exploited by the quantum Fourier transform?
+# ----------------------------------------------------------------------------
+#
+# Why does the Fourier transform prepare a state with such a convenient structure? Let us have a
+# look how it acts on a superposition of inputs :math:`x` for which :math:`f(x)` has the same value, such as
+# :math:`\frac{1}{\sqrt{2}} (|2\rangle + |10\rangle)` (where we translated bitstrings to integers for
+# better readability). The quantum Fourier transform prepares a new state whose amplitudes are
+# proportional to
+#
+# .. math::
+#
+#     \sum_k \left(e^{\frac{2 \pi i 2 k}{12}} + e^{\frac{2 \pi i 10 k}{12}} \right)  |k \rangle.
+#
+# In the exponent you find the values 2 and 10, as well as the size of the group, 12.
+# Somewhat magically, for some :math:`|k \rangle`, all exponential functions in the sum evaluate to :math:`1`, 
+# while for all others, the functions cancel each other out and evaluate exactly to zero.
+
+for k in range(12):
+    res = np.exp(2 * np.pi * 1j * 2 * k / 16) + np.exp(2 * np.pi * 1j * 10 * k / 16)
+    print(f"k={k} --- {np.round(res, 13)}")
+
+#####################################################################
+# This pattern is the same for whichever set of :math:`x` with the same value :math:`f(x)` 
+# (in other words, which "coset") we picked. It is also
+# true for whichever period we encoded, function we chose, and even which (Abelian) group we
+# started with.
+#
+# The "magic" interference, of course, would not surprise a group theorist; it is inherent to the
+# structure of the group. The functions :math:`e^{\frac{2 \pi i x k}{12}}`, which we are so used to
+# see in quantum theory, are nothing but the *characters* of a group: functions that take group
+# elements :math:`x` to complex numbers, and -- a little like eigenvalues in linear algebra -- capture
+# the essentials of the group. The destructive interference we see in the quantum Fourier
+# transform is nothing other than the orthogonality theorem for these characters.
+#
+# It is strangely beautiful how quantum interference leverages the structure of Abelian groups in
+# the quantum Fourier transform. So far this has only found application for rather abstract
+# problems. Even though cryptography, impacted by Shor's algorithm, is what one might consider a
+# "real-world" application, it is an outlier, since problems in cryptography are artificially
+# *constructed* from abstract mathematical theory. A fascinating question is whether "real"
+# real-world applications, like material science or data analysis, could benefit from this
+# remarkable confluence of group structure and quantum theory.
+#
+#
+# References
+# ------------
+#
+# .. [#Aaronson22] 
+#    
+#     Scott Aaronson, "How Much Structure Is Needed for Huge Quantum Speedups?", 
+#     `arXiv:2209.06930 <https://arxiv.org/pdf/2209.06930>`__, 2022
+# 
+# .. [#Childs2010] 
+#    
+#     Andrew Childs, Vim van Dam, "Quantum algorithms for algebraic problems", 
+#     `Reviews of Modern Physics 82.1: 1-52. <https://journals.aps.org/rmp/abstract/10.1103/RevModPhys.82.1>`__, 2010
+#
+# About the author
+# ----------------
+#


Reviews:
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-13T13:56:37Z
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-13T13:56:57Z
user: login: justinpickering
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T14:09:25Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T10:11:26Z
user: login: CatalinaAlbornoz
body: I'm testing the titles to see if this was causing the issue
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T14:21:33Z
user: login: CatalinaAlbornoz
body: I changed the number of hashes in the penultimate section and removed the hashes before the references title
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T14:51:47Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T14:04:22Z
user: login: simidzija
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T14:08:37Z
user: login: simidzija
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T20:36:46Z
user: login: simidzija
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T20:37:49Z
user: login: simidzija
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T21:07:43Z
user: login: simidzija
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T21:29:18Z
user: login: simidzija
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T21:29:39Z
user: login: simidzija
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T21:32:55Z
user: login: simidzija
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T21:39:53Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T06:48:56Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T06:49:27Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T06:50:08Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T06:50:53Z
user: login: simidzija
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T13:29:41Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T14:13:22Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T14:13:28Z
user: login: simidzija
body: Looks great!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T19:43:31Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T06:53:33Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T06:54:55Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T06:56:51Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T07:01:06Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T07:02:00Z
body: Just minor copy things, and then we're ready to go! @mariaschuld I marked it as approved, but please do take a look at the minor copy-edits and commit if you agree :)  
state: APPROVED
author_association: NONE
submitted_at: 2025-04-04T17:17:24Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T06:55:53Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T07:49:21Z
user: login: mariaschuld
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T07:50:35Z
user: login: cognigami
body: Will remove hero image before posting. Otherwise, LGTM.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T16:44:22Z
user: login: cognigami
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T16:45:45Z
user: login: justinpickering
body: Looks great! Just a couple comments before we release (aiming for tomorrow, Apr 16). 
cc @cognigami 
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T02:01:15Z
user: login: justinpickering
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T15:15:50Z

Review comments:
diff_hunk: @@ -0,0 +1,31 @@
+{
+    "title": "Period finding: A problem at the heart of quantum computing",
+    "authors": [
+        {
+            "username": "mariaschuld"
+        },
path: demonstrations/tutorial_period_finding.metadata.json
body: ```suggestion
        }
```
created_at: 2025-03-13T13:56:37Z
author_association: NONE
diff_hunk: @@ -0,0 +1,31 @@
+{
+    "title": "Period finding: A problem at the heart of quantum computing",
+    "authors": [
+        {
+            "username": "mariaschuld"
+        },
path: demonstrations/tutorial_period_finding.metadata.json
body: Not entirely sure, but it might be this comma
created_at: 2025-03-13T13:56:57Z
author_association: NONE
diff_hunk: @@ -0,0 +1,31 @@
+{
+    "title": "Period finding: A problem at the heart of quantum computing",
+    "authors": [
+        {
+            "username": "mariaschuld"
+        }
+    ],
+    "dateOfPublication": "2025-03-31T10:00:00+00:00",
+    "dateOfLastModification": "2025-03-31T10:00:00+00:00",
+    "categories": [
+        "Getting Started",
+        "Quantum Computing",
+    ],
path: demonstrations/tutorial_period_finding.metadata.json
user: login: justinpickering
body: Build error may be due to this trailing comma.

```suggestion
    "categories": [
        "Getting Started",
        "Quantum Computing"
    ],
```
created_at: 2025-03-13T14:09:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,342 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# #################
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
+# values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The period is then the number of integers divided by the number
+# of periods. In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the common denominator as 3, which is the number of periods fitting
+# into 12. From there we can recover 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+################
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+plt.scatter(x, y)
+plt.show()
+
+#####################################################################
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 3` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application this 
+# would be a quantum circuit defined by a sequence of gates.
+
+
+def Oracle(f):
+    """
+    Defines the unitary that implements a function f:{0,..,15} -> {0,..,3}.
+
+    Args:
+      f (func): function to implement
+
+    Returns:
+      ndarray representing the unitary
+    """
+
+    U = np.zeros((2**7, 2**7))
+
+    for x in range(2**4):
+        for f_x in range(2**3):
+            # we know that the initial state has only support on basis states
+            # of the form |x>|0>, and therefore write all necessary information
+            # into the entries of the unitary acting on those states
+            if f_x == 0:
+                i = to_int(to_binary(x, 4) + "0" * 3)
+                j = to_int(to_binary(x, 4) + to_binary(f(x), 3))
+                U[i, j] = 1
+                U[j, i] = 1
+            else:
+                # in all other cases we use trivial entries
+                i = to_int(to_binary(x, 4) + to_binary(f_x, 3))
+                U[i, i] = 1
+
+    # check that this is a unitary
+    assert np.allclose(U @ np.linalg.inv(U), np.eye(2**7))
+
+    return qml.QubitUnitary(U, wires=range(7))
+
+
+#####################################################################
+# Now we're ready to go. Let's implement the famous period finding algorithm. It consists of a
+# quantum routine and a classical postprocessing step. As mentioned, the quantum part is simple: prepare the
+# desired initial state :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum Fourier transform
+# onto the :math:`|x\rangle`-register, and measure in the computational basis. Effectively, this
+# measures in the "Fourier basis", which is where all the magic happens.
+#
+# We only have to get two unique samples, from which we can compute the period of the function. For this 
+# reason we define a device with 2 shots. We also add some snapshots to the circuit that we will look at later.
+
+
+dev = qml.device("default.qubit", wires=7, shots=2)
+
+
+@qml.qnode(dev)
+def circuit():
+    """Circuit to implement the period finding algorithm."""
+
+    for i in range(4):
+        qml.Hadamard(wires=i)
+
+    qml.Snapshot("initial_state")
+
+    Oracle(f)
+
+    qml.Snapshot("loaded_function")
+
+    qml.QFT(wires=range(4))
+
+    qml.Snapshot("fourier_spectrum")
+
+    return qml.sample(wires=range(4))
+
+
+# take two samples from the circuit
+samples = circuit()
+
+#####################################################################
+# The classical post-processing is relatively simple for period finding (whereas 
+# for general hidden subgroup problems it requires some less trivial algebra). 
+# If you are curious about the details, you'll find them in
+# Childs & Dam (2013) [#Childs2010]_, Section IVa.
+
+from fractions import Fraction
+from math import lcm
+
+# convert the bistrings to integers
+sample1_int = to_int("".join(str(s) for s in samples[0]))
+sample2_int = to_int("".join(str(s) for s in samples[1]))
+
+# get the denominator of the fraction representation
+denominator1 = Fraction(sample1_int / (2**4)).denominator
+denominator2 = Fraction(sample2_int / (2**4)).denominator
+
+# get the least common multiple
+result = lcm(denominator1, denominator2)
+
+print(f"Hidden period: {result}")
+
+#####################################################################
+# Yay, we've found the hidden period! Now, of course this is only impressive if we increase the 
+# number of qubits. We can find the hidden period of a function defined on :math:`2^n` values in time
+# :math:`O(n \mathrm{log}(n))`.
+#
+# A peep at the states involved
+###############################
+#
+# Somehow the quantum Fourier transform exposed the information we needed. Let's have a closer
+# look at the states that were prepared, by making use of the snapshots we recorded during the
+# circuit simulation.
+
+dev = qml.device("default.qubit", wires=7, shots=1)
+qnode = qml.QNode(circuit, dev)
+intermediate_states = qml.snapshots(circuit)()
+
+#####################################################################
+# We can plot them as discrete functions, where the size of a point indicates the absolute value
+# of the amplitude.
+
+import matplotlib.patches as mpatches
+
+x = [i for i in range(2**4) for _ in range(2**3)]
+y = list(range(2**3)) * (2**4)
+
+states_to_plot = ["initial_state", "loaded_function", "fourier_spectrum"]
+
+fig, axes = plt.subplots(3, 1, sharex=True, figsize=(7, 6))
+
+for state, ax in zip(states_to_plot, axes):
+
+    real = np.real(intermediate_states[state])
+    imag = np.imag(intermediate_states[state])
+
+    # scale the points by the absolute value of the amplitude
+    sizes_real = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_real.append(200 * np.abs(real[idx]))
+
+    sizes_imag = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_imag.append(200 * np.abs(imag[idx]))
+
+    ax.scatter(np.array(x), y, s=sizes_real, c="green", alpha=0.5)
+    ax.scatter(np.array(x), y, s=sizes_imag, c="pink", alpha=0.5)
+    ax.set_title(state)
+    ax.set_ylabel("f(x)")
+
+plt.xlabel("x")
+green = mpatches.Patch(color="green", label="abs(real part)")
+pink = mpatches.Patch(color="pink", label="abs(imaginary part)")
+plt.legend(handles=[green, pink])
+plt.tight_layout()
+plt.show()
+
+#####################################################################
+# First of all, we see that the oracle really prepared a quantum state that we can interpret as 
+# our periodic function ``f``.
+#
+# Furthermore, the state representing the Fourier spectrum looks actually quite interesting. But
+# the important feature of this state is also clearly visible: Amplitudes concentrate in :math:`x`
+# values that are multiples of 2. And 2 was exactly the amount of periods of 8 that fit into 16.
+#
+# What is the "Abelian structure" exploited by the quantum Fourier transform?
+#############################################################################
+#
+# Why does the Fourier transform prepare a state with such a convenient structure? Let us have a
+# look how it acts on a superposition of inputs :math:`x` for which :math:`f(x)` has the same value, such as
+# :math:`\frac{1}{\sqrt{2}} (|2\rangle + |10\rangle)` (where we translated bitstrings to integers for
+# better readability). The quantum Fourier transform prepares a new state whose amplitudes are
+# proportional to
+#
+# .. math::
+#
+#     \sum_k \left(e^{\frac{2 \pi i 2 k}{12}} + e^{\frac{2 \pi i 10 k}{12}} \right)  |k \rangle.
+#
+# In the exponent you find the values 2 and 10, as well as the size of the group, 12.
+# Somewhat magically, for some :math:`|k \rangle`, all exponential functions in the sum evaluate to :math:`1`, 
+# while for all others, the functions cancel each other out and evaluate exactly to zero.
+
+for k in range(12):
+    res = np.exp(2 * np.pi * 1j * 2 * k / 16) + np.exp(2 * np.pi * 1j * 10 * k / 16)
+    print(f"k={k} --- {np.round(res, 13)}")
+
+#####################################################################
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: Something is wrong in the formatting here. @alvaro-at-xanadu do you have an idea by any chance? :thinking: 
created_at: 2025-03-18T10:11:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,345 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
path: demonstrations/tutorial_period_finding.py
user: login: CatalinaAlbornoz
body: This image looks a bit blurry to me. Is this just me?
created_at: 2025-03-19T13:57:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,345 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# #################
path: demonstrations/tutorial_period_finding.py
user: login: CatalinaAlbornoz
body: ```suggestion
# Problem statement
# -------------------
```
created_at: 2025-03-19T14:16:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,345 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# #################
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
+# values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The period is then the number of integers divided by the number
+# of periods. In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the common denominator as 3, which is the number of periods fitting
+# into 12. From there we can recover 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+################
path: demonstrations/tutorial_period_finding.py
user: login: CatalinaAlbornoz
body: ```suggestion
# Implementation
# ----------------
```
created_at: 2025-03-19T14:17:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,345 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# #################
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
+# values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The period is then the number of integers divided by the number
+# of periods. In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the common denominator as 3, which is the number of periods fitting
+# into 12. From there we can recover 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+################
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+plt.scatter(x, y)
+plt.show()
+
+#####################################################################
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 3` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application this 
+# would be a quantum circuit defined by a sequence of gates.
+
+
+def Oracle(f):
+    """
+    Defines the unitary that implements a function f:{0,..,15} -> {0,..,3}.
+
+    Args:
+      f (func): function to implement
+
+    Returns:
+      ndarray representing the unitary
+    """
+
+    U = np.zeros((2**7, 2**7))
+
+    for x in range(2**4):
+        for f_x in range(2**3):
+            # we know that the initial state has only support on basis states
+            # of the form |x>|0>, and therefore write all necessary information
+            # into the entries of the unitary acting on those states
+            if f_x == 0:
+                i = to_int(to_binary(x, 4) + "0" * 3)
+                j = to_int(to_binary(x, 4) + to_binary(f(x), 3))
+                U[i, j] = 1
+                U[j, i] = 1
+            else:
+                # in all other cases we use trivial entries
+                i = to_int(to_binary(x, 4) + to_binary(f_x, 3))
+                U[i, i] = 1
+
+    # check that this is a unitary
+    assert np.allclose(U @ np.linalg.inv(U), np.eye(2**7))
+
+    return qml.QubitUnitary(U, wires=range(7))
+
+
+#####################################################################
+# Now we're ready to go. Let's implement the famous period finding algorithm. It consists of a
+# quantum routine and a classical postprocessing step. As mentioned, the quantum part is simple: prepare the
+# desired initial state :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum Fourier transform
+# onto the :math:`|x\rangle`-register, and measure in the computational basis. Effectively, this
+# measures in the "Fourier basis", which is where all the magic happens.
+#
+# We only have to get two unique samples, from which we can compute the period of the function. For this 
+# reason we define a device with 2 shots. We also add some snapshots to the circuit that we will look at later.
+
+
+dev = qml.device("default.qubit", wires=7, shots=2)
+
+
+@qml.qnode(dev)
+def circuit():
+    """Circuit to implement the period finding algorithm."""
+
+    for i in range(4):
+        qml.Hadamard(wires=i)
+
+    qml.Snapshot("initial_state")
+
+    Oracle(f)
+
+    qml.Snapshot("loaded_function")
+
+    qml.QFT(wires=range(4))
+
+    qml.Snapshot("fourier_spectrum")
+
+    return qml.sample(wires=range(4))
+
+
+# take two samples from the circuit
+samples = circuit()
+
+#####################################################################
+# The classical post-processing is relatively simple for period finding (whereas 
+# for general hidden subgroup problems it requires some less trivial algebra). 
+# If you are curious about the details, you'll find them in
+# Childs & Dam (2013) [#Childs2010]_, Section IVa.
+
+from fractions import Fraction
+from math import lcm
+
+# convert the bistrings to integers
+sample1_int = to_int("".join(str(s) for s in samples[0]))
+sample2_int = to_int("".join(str(s) for s in samples[1]))
+
+# get the denominator of the fraction representation
+denominator1 = Fraction(sample1_int / (2**4)).denominator
+denominator2 = Fraction(sample2_int / (2**4)).denominator
+
+# get the least common multiple
+result = lcm(denominator1, denominator2)
+
+print(f"Hidden period: {result}")
+
+#####################################################################
+# Yay, we've found the hidden period! Now, of course this is only impressive if we increase the 
+# number of qubits. We can find the hidden period of a function defined on :math:`2^n` values in time
+# :math:`O(n \mathrm{log}(n))`.
+
+
+#####################################################################
+# A peep at the states involved
+###############################
+#
+# Somehow the quantum Fourier transform exposed the information we needed. Let's have a closer
+# look at the states that were prepared, by making use of the snapshots we recorded during the
+# circuit simulation.
+
+dev = qml.device("default.qubit", wires=7, shots=1)
+qnode = qml.QNode(circuit, dev)
+intermediate_states = qml.snapshots(circuit)()
+
+#####################################################################
+# We can plot them as discrete functions, where the size of a point indicates the absolute value
+# of the amplitude.
+
+import matplotlib.patches as mpatches
+
+x = [i for i in range(2**4) for _ in range(2**3)]
+y = list(range(2**3)) * (2**4)
+
+states_to_plot = ["initial_state", "loaded_function", "fourier_spectrum"]
+
+fig, axes = plt.subplots(3, 1, sharex=True, figsize=(7, 6))
+
+for state, ax in zip(states_to_plot, axes):
+
+    real = np.real(intermediate_states[state])
+    imag = np.imag(intermediate_states[state])
+
+    # scale the points by the absolute value of the amplitude
+    sizes_real = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_real.append(200 * np.abs(real[idx]))
+
+    sizes_imag = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_imag.append(200 * np.abs(imag[idx]))
+
+    ax.scatter(np.array(x), y, s=sizes_real, c="green", alpha=0.5)
+    ax.scatter(np.array(x), y, s=sizes_imag, c="pink", alpha=0.5)
+    ax.set_title(state)
+    ax.set_ylabel("f(x)")
+
+plt.xlabel("x")
+green = mpatches.Patch(color="green", label="abs(real part)")
+pink = mpatches.Patch(color="pink", label="abs(imaginary part)")
+plt.legend(handles=[green, pink])
+plt.tight_layout()
+plt.show()
+
+#####################################################################
+# First of all, we see that the oracle really prepared a quantum state that we can interpret as 
+# our periodic function ``f``.
+#
+# Furthermore, the state representing the Fourier spectrum looks actually quite interesting. But
+# the important feature of this state is also clearly visible: Amplitudes concentrate in :math:`x`
+# values that are multiples of 2. And 2 was exactly the amount of periods of 8 that fit into 16.
+
+#############################################################################
+# What is the "Abelian structure" exploited by the quantum Fourier transform?
+#############################################################################
path: demonstrations/tutorial_period_finding.py
user: login: CatalinaAlbornoz
body: ```suggestion
# What is the "Abelian structure" exploited by the quantum Fourier transform?
# ----------------------------------------------------------------------------
```
created_at: 2025-03-19T14:18:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,345 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# #################
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
+# values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The period is then the number of integers divided by the number
+# of periods. In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the common denominator as 3, which is the number of periods fitting
+# into 12. From there we can recover 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+################
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+plt.scatter(x, y)
+plt.show()
+
+#####################################################################
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 3` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application this 
+# would be a quantum circuit defined by a sequence of gates.
+
+
+def Oracle(f):
+    """
+    Defines the unitary that implements a function f:{0,..,15} -> {0,..,3}.
+
+    Args:
+      f (func): function to implement
+
+    Returns:
+      ndarray representing the unitary
+    """
+
+    U = np.zeros((2**7, 2**7))
+
+    for x in range(2**4):
+        for f_x in range(2**3):
+            # we know that the initial state has only support on basis states
+            # of the form |x>|0>, and therefore write all necessary information
+            # into the entries of the unitary acting on those states
+            if f_x == 0:
+                i = to_int(to_binary(x, 4) + "0" * 3)
+                j = to_int(to_binary(x, 4) + to_binary(f(x), 3))
+                U[i, j] = 1
+                U[j, i] = 1
+            else:
+                # in all other cases we use trivial entries
+                i = to_int(to_binary(x, 4) + to_binary(f_x, 3))
+                U[i, i] = 1
+
+    # check that this is a unitary
+    assert np.allclose(U @ np.linalg.inv(U), np.eye(2**7))
+
+    return qml.QubitUnitary(U, wires=range(7))
+
+
+#####################################################################
+# Now we're ready to go. Let's implement the famous period finding algorithm. It consists of a
+# quantum routine and a classical postprocessing step. As mentioned, the quantum part is simple: prepare the
+# desired initial state :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum Fourier transform
+# onto the :math:`|x\rangle`-register, and measure in the computational basis. Effectively, this
+# measures in the "Fourier basis", which is where all the magic happens.
+#
+# We only have to get two unique samples, from which we can compute the period of the function. For this 
+# reason we define a device with 2 shots. We also add some snapshots to the circuit that we will look at later.
+
+
+dev = qml.device("default.qubit", wires=7, shots=2)
+
+
+@qml.qnode(dev)
+def circuit():
+    """Circuit to implement the period finding algorithm."""
+
+    for i in range(4):
+        qml.Hadamard(wires=i)
+
+    qml.Snapshot("initial_state")
+
+    Oracle(f)
+
+    qml.Snapshot("loaded_function")
+
+    qml.QFT(wires=range(4))
+
+    qml.Snapshot("fourier_spectrum")
+
+    return qml.sample(wires=range(4))
+
+
+# take two samples from the circuit
+samples = circuit()
+
+#####################################################################
+# The classical post-processing is relatively simple for period finding (whereas 
+# for general hidden subgroup problems it requires some less trivial algebra). 
+# If you are curious about the details, you'll find them in
+# Childs & Dam (2013) [#Childs2010]_, Section IVa.
+
+from fractions import Fraction
+from math import lcm
+
+# convert the bistrings to integers
+sample1_int = to_int("".join(str(s) for s in samples[0]))
+sample2_int = to_int("".join(str(s) for s in samples[1]))
+
+# get the denominator of the fraction representation
+denominator1 = Fraction(sample1_int / (2**4)).denominator
+denominator2 = Fraction(sample2_int / (2**4)).denominator
+
+# get the least common multiple
+result = lcm(denominator1, denominator2)
+
+print(f"Hidden period: {result}")
+
+#####################################################################
+# Yay, we've found the hidden period! Now, of course this is only impressive if we increase the 
+# number of qubits. We can find the hidden period of a function defined on :math:`2^n` values in time
+# :math:`O(n \mathrm{log}(n))`.
+
+
+#####################################################################
+# A peep at the states involved
+###############################
path: demonstrations/tutorial_period_finding.py
user: login: CatalinaAlbornoz
body: ```suggestion
# A peep at the states involved
# -----------------------------
```
created_at: 2025-03-19T14:19:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,345 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# #################
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
+# values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The period is then the number of integers divided by the number
+# of periods. In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the common denominator as 3, which is the number of periods fitting
+# into 12. From there we can recover 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+################
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+plt.scatter(x, y)
+plt.show()
+
+#####################################################################
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 3` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application this 
+# would be a quantum circuit defined by a sequence of gates.
+
+
+def Oracle(f):
+    """
+    Defines the unitary that implements a function f:{0,..,15} -> {0,..,3}.
+
+    Args:
+      f (func): function to implement
+
+    Returns:
+      ndarray representing the unitary
+    """
+
+    U = np.zeros((2**7, 2**7))
+
+    for x in range(2**4):
+        for f_x in range(2**3):
+            # we know that the initial state has only support on basis states
+            # of the form |x>|0>, and therefore write all necessary information
+            # into the entries of the unitary acting on those states
+            if f_x == 0:
+                i = to_int(to_binary(x, 4) + "0" * 3)
+                j = to_int(to_binary(x, 4) + to_binary(f(x), 3))
+                U[i, j] = 1
+                U[j, i] = 1
+            else:
+                # in all other cases we use trivial entries
+                i = to_int(to_binary(x, 4) + to_binary(f_x, 3))
+                U[i, i] = 1
+
+    # check that this is a unitary
+    assert np.allclose(U @ np.linalg.inv(U), np.eye(2**7))
+
+    return qml.QubitUnitary(U, wires=range(7))
+
+
+#####################################################################
+# Now we're ready to go. Let's implement the famous period finding algorithm. It consists of a
+# quantum routine and a classical postprocessing step. As mentioned, the quantum part is simple: prepare the
+# desired initial state :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum Fourier transform
+# onto the :math:`|x\rangle`-register, and measure in the computational basis. Effectively, this
+# measures in the "Fourier basis", which is where all the magic happens.
+#
+# We only have to get two unique samples, from which we can compute the period of the function. For this 
+# reason we define a device with 2 shots. We also add some snapshots to the circuit that we will look at later.
+
+
+dev = qml.device("default.qubit", wires=7, shots=2)
+
+
+@qml.qnode(dev)
+def circuit():
+    """Circuit to implement the period finding algorithm."""
+
+    for i in range(4):
+        qml.Hadamard(wires=i)
+
+    qml.Snapshot("initial_state")
+
+    Oracle(f)
+
+    qml.Snapshot("loaded_function")
+
+    qml.QFT(wires=range(4))
+
+    qml.Snapshot("fourier_spectrum")
+
+    return qml.sample(wires=range(4))
+
+
+# take two samples from the circuit
+samples = circuit()
+
+#####################################################################
+# The classical post-processing is relatively simple for period finding (whereas 
+# for general hidden subgroup problems it requires some less trivial algebra). 
+# If you are curious about the details, you'll find them in
+# Childs & Dam (2013) [#Childs2010]_, Section IVa.
+
+from fractions import Fraction
+from math import lcm
+
+# convert the bistrings to integers
+sample1_int = to_int("".join(str(s) for s in samples[0]))
+sample2_int = to_int("".join(str(s) for s in samples[1]))
+
+# get the denominator of the fraction representation
+denominator1 = Fraction(sample1_int / (2**4)).denominator
+denominator2 = Fraction(sample2_int / (2**4)).denominator
+
+# get the least common multiple
+result = lcm(denominator1, denominator2)
+
+print(f"Hidden period: {result}")
+
+#####################################################################
+# Yay, we've found the hidden period! Now, of course this is only impressive if we increase the 
+# number of qubits. We can find the hidden period of a function defined on :math:`2^n` values in time
+# :math:`O(n \mathrm{log}(n))`.
+
+
+#####################################################################
+# A peep at the states involved
+###############################
+#
+# Somehow the quantum Fourier transform exposed the information we needed. Let's have a closer
+# look at the states that were prepared, by making use of the snapshots we recorded during the
+# circuit simulation.
+
+dev = qml.device("default.qubit", wires=7, shots=1)
+qnode = qml.QNode(circuit, dev)
+intermediate_states = qml.snapshots(circuit)()
+
+#####################################################################
+# We can plot them as discrete functions, where the size of a point indicates the absolute value
+# of the amplitude.
+
+import matplotlib.patches as mpatches
+
+x = [i for i in range(2**4) for _ in range(2**3)]
+y = list(range(2**3)) * (2**4)
+
+states_to_plot = ["initial_state", "loaded_function", "fourier_spectrum"]
+
+fig, axes = plt.subplots(3, 1, sharex=True, figsize=(7, 6))
+
+for state, ax in zip(states_to_plot, axes):
+
+    real = np.real(intermediate_states[state])
+    imag = np.imag(intermediate_states[state])
+
+    # scale the points by the absolute value of the amplitude
+    sizes_real = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_real.append(200 * np.abs(real[idx]))
+
+    sizes_imag = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_imag.append(200 * np.abs(imag[idx]))
+
+    ax.scatter(np.array(x), y, s=sizes_real, c="green", alpha=0.5)
+    ax.scatter(np.array(x), y, s=sizes_imag, c="pink", alpha=0.5)
+    ax.set_title(state)
+    ax.set_ylabel("f(x)")
+
+plt.xlabel("x")
+green = mpatches.Patch(color="green", label="abs(real part)")
+pink = mpatches.Patch(color="pink", label="abs(imaginary part)")
+plt.legend(handles=[green, pink])
+plt.tight_layout()
+plt.show()
+
+#####################################################################
+# First of all, we see that the oracle really prepared a quantum state that we can interpret as 
+# our periodic function ``f``.
+#
+# Furthermore, the state representing the Fourier spectrum looks actually quite interesting. But
+# the important feature of this state is also clearly visible: Amplitudes concentrate in :math:`x`
+# values that are multiples of 2. And 2 was exactly the amount of periods of 8 that fit into 16.
+
+#############################################################################
+# What is the "Abelian structure" exploited by the quantum Fourier transform?
+#############################################################################
+#
+# Why does the Fourier transform prepare a state with such a convenient structure? Let us have a
+# look how it acts on a superposition of inputs :math:`x` for which :math:`f(x)` has the same value, such as
+# :math:`\frac{1}{\sqrt{2}} (|2\rangle + |10\rangle)` (where we translated bitstrings to integers for
+# better readability). The quantum Fourier transform prepares a new state whose amplitudes are
+# proportional to
+#
+# .. math::
+#
+#     \sum_k \left(e^{\frac{2 \pi i 2 k}{12}} + e^{\frac{2 \pi i 10 k}{12}} \right)  |k \rangle.
+#
+# In the exponent you find the values 2 and 10, as well as the size of the group, 12.
+# Somewhat magically, for some :math:`|k \rangle`, all exponential functions in the sum evaluate to :math:`1`, 
+# while for all others, the functions cancel each other out and evaluate exactly to zero.
+
+for k in range(12):
+    res = np.exp(2 * np.pi * 1j * 2 * k / 16) + np.exp(2 * np.pi * 1j * 10 * k / 16)
+    print(f"k={k} --- {np.round(res, 13)}")
+
+#####################################################################
+# This pattern is the same for whichever set of :math:`x` with the same value :math:`f(x)` 
+# (in other words, which "coset") we picked. It is also
+# true for whichever period we encoded, function we chose, and even which (Abelian) group we
+# started with.
+#
+# The "magic" interference, of course, would not surprise a group theorist; it is inherent to the
+# structure of the group. The functions :math:`e^{\frac{2 \pi i x k}{12}}`, which we are so used to
+# see in quantum theory, are nothing but the *characters* of a group: functions that take group
+# elements :math:`x` to complex numbers, and -- a little like eigenvalues in linear algebra -- capture
+# the essentials of the group. The destructive interference we see in the quantum Fourier
+# transform is nothing other than the orthogonality theorem for these characters.
+#
+# It is strangely beautiful how quantum interference leverages the structure of Abelian groups in
+# the quantum Fourier transform. So far this has only found application for rather abstract
+# problems. Even though cryptography, impacted by Shor's algorithm, is what one might consider a
+# "real-world" application, it is an outlier, since problems in cryptography are artificially
+# *constructed* from abstract mathematical theory. A fascinating question is whether "real"
+# real-world applications, like material science or data analysis, could benefit from this
+# remarkable confluence of group structure and quantum theory.
+#
+#
+##############################################################################
+# References
+# ##########
path: demonstrations/tutorial_period_finding.py
user: login: CatalinaAlbornoz
body: ```suggestion
# References
# ------------
```
created_at: 2025-03-19T14:20:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,345 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
+# values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The period is then the number of integers divided by the number
+# of periods. In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the common denominator as 3, which is the number of periods fitting
+# into 12. From there we can recover 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+# ----------------
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+plt.scatter(x, y)
+plt.show()
+
+#####################################################################
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 3` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application this 
+# would be a quantum circuit defined by a sequence of gates.
+
+
+def Oracle(f):
+    """
+    Defines the unitary that implements a function f:{0,..,15} -> {0,..,3}.
+
+    Args:
+      f (func): function to implement
+
+    Returns:
+      ndarray representing the unitary
+    """
+
+    U = np.zeros((2**7, 2**7))
+
+    for x in range(2**4):
+        for f_x in range(2**3):
+            # we know that the initial state has only support on basis states
+            # of the form |x>|0>, and therefore write all necessary information
+            # into the entries of the unitary acting on those states
+            if f_x == 0:
+                i = to_int(to_binary(x, 4) + "0" * 3)
+                j = to_int(to_binary(x, 4) + to_binary(f(x), 3))
+                U[i, j] = 1
+                U[j, i] = 1
+            else:
+                # in all other cases we use trivial entries
+                i = to_int(to_binary(x, 4) + to_binary(f_x, 3))
+                U[i, i] = 1
+
+    # check that this is a unitary
+    assert np.allclose(U @ np.linalg.inv(U), np.eye(2**7))
+
+    return qml.QubitUnitary(U, wires=range(7))
+
+
+#####################################################################
+# Now we're ready to go. Let's implement the famous period finding algorithm. It consists of a
+# quantum routine and a classical postprocessing step. As mentioned, the quantum part is simple: prepare the
+# desired initial state :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum Fourier transform
+# onto the :math:`|x\rangle`-register, and measure in the computational basis. Effectively, this
+# measures in the "Fourier basis", which is where all the magic happens.
+#
+# We only have to get two unique samples, from which we can compute the period of the function. For this 
+# reason we define a device with 2 shots. We also add some snapshots to the circuit that we will look at later.
+
+
+dev = qml.device("default.qubit", wires=7, shots=2)
+
+
+@qml.qnode(dev)
+def circuit():
+    """Circuit to implement the period finding algorithm."""
+
+    for i in range(4):
+        qml.Hadamard(wires=i)
+
+    qml.Snapshot("initial_state")
+
+    Oracle(f)
+
+    qml.Snapshot("loaded_function")
+
+    qml.QFT(wires=range(4))
+
+    qml.Snapshot("fourier_spectrum")
+
+    return qml.sample(wires=range(4))
+
+
+# take two samples from the circuit
+samples = circuit()
+
+#####################################################################
+# The classical post-processing is relatively simple for period finding (whereas 
+# for general hidden subgroup problems it requires some less trivial algebra). 
+# If you are curious about the details, you'll find them in
+# Childs & Dam (2013) [#Childs2010]_, Section IVa.
+
+from fractions import Fraction
+from math import lcm
+
+# convert the bistrings to integers
+sample1_int = to_int("".join(str(s) for s in samples[0]))
+sample2_int = to_int("".join(str(s) for s in samples[1]))
+
+# get the denominator of the fraction representation
+denominator1 = Fraction(sample1_int / (2**4)).denominator
+denominator2 = Fraction(sample2_int / (2**4)).denominator
+
+# get the least common multiple
+result = lcm(denominator1, denominator2)
+
+print(f"Hidden period: {result}")
+
+#####################################################################
+# Yay, we've found the hidden period! Now, of course this is only impressive if we increase the 
+# number of qubits. We can find the hidden period of a function defined on :math:`2^n` values in time
+# :math:`O(n \mathrm{log}(n))`.
+
+
+#####################################################################
+# A peep at the states involved
+# -----------------------------
+#
+# Somehow the quantum Fourier transform exposed the information we needed. Let's have a closer
+# look at the states that were prepared, by making use of the snapshots we recorded during the
+# circuit simulation.
+
+dev = qml.device("default.qubit", wires=7, shots=1)
+qnode = qml.QNode(circuit, dev)
+intermediate_states = qml.snapshots(circuit)()
+
+#####################################################################
+# We can plot them as discrete functions, where the size of a point indicates the absolute value
+# of the amplitude.
+
+import matplotlib.patches as mpatches
+
+x = [i for i in range(2**4) for _ in range(2**3)]
+y = list(range(2**3)) * (2**4)
+
+states_to_plot = ["initial_state", "loaded_function", "fourier_spectrum"]
+
+fig, axes = plt.subplots(3, 1, sharex=True, figsize=(7, 6))
+
+for state, ax in zip(states_to_plot, axes):
+
+    real = np.real(intermediate_states[state])
+    imag = np.imag(intermediate_states[state])
+
+    # scale the points by the absolute value of the amplitude
+    sizes_real = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_real.append(200 * np.abs(real[idx]))
+
+    sizes_imag = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_imag.append(200 * np.abs(imag[idx]))
+
+    ax.scatter(np.array(x), y, s=sizes_real, c="green", alpha=0.5)
+    ax.scatter(np.array(x), y, s=sizes_imag, c="pink", alpha=0.5)
+    ax.set_title(state)
+    ax.set_ylabel("f(x)")
+
+plt.xlabel("x")
+green = mpatches.Patch(color="green", label="abs(real part)")
+pink = mpatches.Patch(color="pink", label="abs(imaginary part)")
+plt.legend(handles=[green, pink])
+plt.tight_layout()
+plt.show()
+
+#####################################################################
+# First of all, we see that the oracle really prepared a quantum state that we can interpret as 
+# our periodic function ``f``.
+#
+# Furthermore, the state representing the Fourier spectrum looks actually quite interesting. But
+# the important feature of this state is also clearly visible: Amplitudes concentrate in :math:`x`
+# values that are multiples of 2. And 2 was exactly the amount of periods of 8 that fit into 16.
+
+#############################################################################
path: demonstrations/tutorial_period_finding.py
user: login: CatalinaAlbornoz
body: ```suggestion
#####################################################################
```
created_at: 2025-03-19T14:49:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,345 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
+# values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The period is then the number of integers divided by the number
+# of periods. In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the common denominator as 3, which is the number of periods fitting
+# into 12. From there we can recover 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+# ----------------
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+plt.scatter(x, y)
+plt.show()
+
+#####################################################################
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 3` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application this 
+# would be a quantum circuit defined by a sequence of gates.
+
+
+def Oracle(f):
+    """
+    Defines the unitary that implements a function f:{0,..,15} -> {0,..,3}.
+
+    Args:
+      f (func): function to implement
+
+    Returns:
+      ndarray representing the unitary
+    """
+
+    U = np.zeros((2**7, 2**7))
+
+    for x in range(2**4):
+        for f_x in range(2**3):
+            # we know that the initial state has only support on basis states
+            # of the form |x>|0>, and therefore write all necessary information
+            # into the entries of the unitary acting on those states
+            if f_x == 0:
+                i = to_int(to_binary(x, 4) + "0" * 3)
+                j = to_int(to_binary(x, 4) + to_binary(f(x), 3))
+                U[i, j] = 1
+                U[j, i] = 1
+            else:
+                # in all other cases we use trivial entries
+                i = to_int(to_binary(x, 4) + to_binary(f_x, 3))
+                U[i, i] = 1
+
+    # check that this is a unitary
+    assert np.allclose(U @ np.linalg.inv(U), np.eye(2**7))
+
+    return qml.QubitUnitary(U, wires=range(7))
+
+
+#####################################################################
+# Now we're ready to go. Let's implement the famous period finding algorithm. It consists of a
+# quantum routine and a classical postprocessing step. As mentioned, the quantum part is simple: prepare the
+# desired initial state :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum Fourier transform
+# onto the :math:`|x\rangle`-register, and measure in the computational basis. Effectively, this
+# measures in the "Fourier basis", which is where all the magic happens.
+#
+# We only have to get two unique samples, from which we can compute the period of the function. For this 
+# reason we define a device with 2 shots. We also add some snapshots to the circuit that we will look at later.
+
+
+dev = qml.device("default.qubit", wires=7, shots=2)
+
+
+@qml.qnode(dev)
+def circuit():
+    """Circuit to implement the period finding algorithm."""
+
+    for i in range(4):
+        qml.Hadamard(wires=i)
+
+    qml.Snapshot("initial_state")
+
+    Oracle(f)
+
+    qml.Snapshot("loaded_function")
+
+    qml.QFT(wires=range(4))
+
+    qml.Snapshot("fourier_spectrum")
+
+    return qml.sample(wires=range(4))
+
+
+# take two samples from the circuit
+samples = circuit()
+
+#####################################################################
+# The classical post-processing is relatively simple for period finding (whereas 
+# for general hidden subgroup problems it requires some less trivial algebra). 
+# If you are curious about the details, you'll find them in
+# Childs & Dam (2013) [#Childs2010]_, Section IVa.
+
+from fractions import Fraction
+from math import lcm
+
+# convert the bistrings to integers
+sample1_int = to_int("".join(str(s) for s in samples[0]))
+sample2_int = to_int("".join(str(s) for s in samples[1]))
+
+# get the denominator of the fraction representation
+denominator1 = Fraction(sample1_int / (2**4)).denominator
+denominator2 = Fraction(sample2_int / (2**4)).denominator
+
+# get the least common multiple
+result = lcm(denominator1, denominator2)
+
+print(f"Hidden period: {result}")
+
+#####################################################################
+# Yay, we've found the hidden period! Now, of course this is only impressive if we increase the 
+# number of qubits. We can find the hidden period of a function defined on :math:`2^n` values in time
+# :math:`O(n \mathrm{log}(n))`.
+
+
+#####################################################################
+# A peep at the states involved
+# -----------------------------
+#
+# Somehow the quantum Fourier transform exposed the information we needed. Let's have a closer
+# look at the states that were prepared, by making use of the snapshots we recorded during the
+# circuit simulation.
+
+dev = qml.device("default.qubit", wires=7, shots=1)
+qnode = qml.QNode(circuit, dev)
+intermediate_states = qml.snapshots(circuit)()
+
+#####################################################################
+# We can plot them as discrete functions, where the size of a point indicates the absolute value
+# of the amplitude.
+
+import matplotlib.patches as mpatches
+
+x = [i for i in range(2**4) for _ in range(2**3)]
+y = list(range(2**3)) * (2**4)
+
+states_to_plot = ["initial_state", "loaded_function", "fourier_spectrum"]
+
+fig, axes = plt.subplots(3, 1, sharex=True, figsize=(7, 6))
+
+for state, ax in zip(states_to_plot, axes):
+
+    real = np.real(intermediate_states[state])
+    imag = np.imag(intermediate_states[state])
+
+    # scale the points by the absolute value of the amplitude
+    sizes_real = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_real.append(200 * np.abs(real[idx]))
+
+    sizes_imag = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_imag.append(200 * np.abs(imag[idx]))
+
+    ax.scatter(np.array(x), y, s=sizes_real, c="green", alpha=0.5)
+    ax.scatter(np.array(x), y, s=sizes_imag, c="pink", alpha=0.5)
+    ax.set_title(state)
+    ax.set_ylabel("f(x)")
+
+plt.xlabel("x")
+green = mpatches.Patch(color="green", label="abs(real part)")
+pink = mpatches.Patch(color="pink", label="abs(imaginary part)")
+plt.legend(handles=[green, pink])
+plt.tight_layout()
+plt.show()
+
+#####################################################################
+# First of all, we see that the oracle really prepared a quantum state that we can interpret as 
+# our periodic function ``f``.
+#
+# Furthermore, the state representing the Fourier spectrum looks actually quite interesting. But
+# the important feature of this state is also clearly visible: Amplitudes concentrate in :math:`x`
+# values that are multiples of 2. And 2 was exactly the amount of periods of 8 that fit into 16.
+
+#############################################################################
+# What is the "Abelian structure" exploited by the quantum Fourier transform?
+# ----------------------------------------------------------------------------
+#
+# Why does the Fourier transform prepare a state with such a convenient structure? Let us have a
+# look how it acts on a superposition of inputs :math:`x` for which :math:`f(x)` has the same value, such as
+# :math:`\frac{1}{\sqrt{2}} (|2\rangle + |10\rangle)` (where we translated bitstrings to integers for
+# better readability). The quantum Fourier transform prepares a new state whose amplitudes are
+# proportional to
+#
+# .. math::
+#
+#     \sum_k \left(e^{\frac{2 \pi i 2 k}{12}} + e^{\frac{2 \pi i 10 k}{12}} \right)  |k \rangle.
+#
+# In the exponent you find the values 2 and 10, as well as the size of the group, 12.
+# Somewhat magically, for some :math:`|k \rangle`, all exponential functions in the sum evaluate to :math:`1`, 
+# while for all others, the functions cancel each other out and evaluate exactly to zero.
+
+for k in range(12):
+    res = np.exp(2 * np.pi * 1j * 2 * k / 16) + np.exp(2 * np.pi * 1j * 10 * k / 16)
+    print(f"k={k} --- {np.round(res, 13)}")
+
+#####################################################################
+# This pattern is the same for whichever set of :math:`x` with the same value :math:`f(x)` 
+# (in other words, which "coset") we picked. It is also
+# true for whichever period we encoded, function we chose, and even which (Abelian) group we
+# started with.
+#
+# The "magic" interference, of course, would not surprise a group theorist; it is inherent to the
+# structure of the group. The functions :math:`e^{\frac{2 \pi i x k}{12}}`, which we are so used to
+# see in quantum theory, are nothing but the *characters* of a group: functions that take group
+# elements :math:`x` to complex numbers, and -- a little like eigenvalues in linear algebra -- capture
+# the essentials of the group. The destructive interference we see in the quantum Fourier
+# transform is nothing other than the orthogonality theorem for these characters.
+#
+# It is strangely beautiful how quantum interference leverages the structure of Abelian groups in
+# the quantum Fourier transform. So far this has only found application for rather abstract
+# problems. Even though cryptography, impacted by Shor's algorithm, is what one might consider a
+# "real-world" application, it is an outlier, since problems in cryptography are artificially
+# *constructed* from abstract mathematical theory. A fascinating question is whether "real"
+# real-world applications, like material science or data analysis, could benefit from this
+# remarkable confluence of group structure and quantum theory.
+#
+#
+##############################################################################
path: demonstrations/tutorial_period_finding.py
user: login: CatalinaAlbornoz
body: ```suggestion
```
created_at: 2025-03-19T14:50:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,344 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: ```suggestion
You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable NUMBER of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
```
Rather say this
created_at: 2025-03-20T14:04:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,344 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
path: demonstrations/tutorial_period_finding.py
user: login: simidzija
body: Test comment
created_at: 2025-03-20T14:08:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,344 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
path: demonstrations/tutorial_period_finding.py
user: login: simidzija
body: 2000s rather than 2000's
created_at: 2025-03-20T20:36:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,344 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
path: demonstrations/tutorial_period_finding.py
user: login: simidzija
body: an integer valued function
created_at: 2025-03-20T20:37:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,344 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
path: demonstrations/tutorial_period_finding.py
user: login: simidzija
body: I would add something like:

The definition of a group in math is a set that has:
1) an addition operation "+" that maps two elements a and b of the set into a third element of the set, c = a + b
2) an "identity element" e such that e + a = a for any element a, and
3) an inverse -a for every element a, such that a + (-a) = e. 

A group is called "Abelian" if a + b = b + a for all a and b, otherwise it is called non-Abelian. In general, Abelian groups are simpler to work with than non-Abelian groups.

With this definition we can easily see that the group of integers {0,1,...,11} with addition modulo 12 forms an Abelian group: there is an addition operation such that a + b = b + a, the element 0 is the identity element since 0 + a = a for all a, and the inverse of element a modulo 12 is 12 - a. 
created_at: 2025-03-20T21:07:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,344 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
+# values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The period is then the number of integers divided by the number
+# of periods. In the example above, the quantum algorithm would return random bistrings that can be
path: demonstrations/tutorial_period_finding.py
user: login: simidzija
body: Instead of "The period is then the number of integers divided by the number of periods" say "The number of periods is then the biggest number that divides all the measurement outcomes, and the period is the number of integers divided by the number of periods."
created_at: 2025-03-20T21:29:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,344 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
+# values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The period is then the number of integers divided by the number
+# of periods. In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the common denominator as 3, which is the number of periods fitting
path: demonstrations/tutorial_period_finding.py
user: login: simidzija
body: Instead of "common denominator" say "largest common divisor"
created_at: 2025-03-20T21:29:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,344 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
+# values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The period is then the number of integers divided by the number
+# of periods. In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the common denominator as 3, which is the number of periods fitting
+# into 12. From there we can recover 12/3 = 4.
path: demonstrations/tutorial_period_finding.py
user: login: simidzija
body: From there we can recover the number of periods as 12/3 = 4.
created_at: 2025-03-20T21:32:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,344 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
+# values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The period is then the number of integers divided by the number
+# of periods. In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the common denominator as 3, which is the number of periods fitting
+# into 12. From there we can recover 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+# ----------------
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+plt.scatter(x, y)
+plt.show()
+
+#####################################################################
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 3` can be expressed by preparing a state that has a
path: demonstrations/tutorial_period_finding.py
user: login: simidzija
body: f(2) = 2 right?
created_at: 2025-03-20T21:39:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,344 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: ```suggestion
You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
```
created_at: 2025-03-24T06:48:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,344 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: ```suggestion
However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
```
created_at: 2025-03-24T06:49:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,344 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: Good idea, I'll make this a blue box!
created_at: 2025-03-24T06:50:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,344 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000's.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer-valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. The
+# values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The period is then the number of integers divided by the number
+# of periods. In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the common denominator as 3, which is the number of periods fitting
+# into 12. From there we can recover 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+# ----------------
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+plt.scatter(x, y)
+plt.show()
+
+#####################################################################
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 3` can be expressed by preparing a state that has a
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: Ah, I didn't refer to the example function, but see how this could be confusing here! Thanks.
created_at: 2025-03-24T06:50:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,361 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. 
+#
+# .. admonition:: Abelian group
+#     :class: note
+#
+#     A group is a set of elements that has:
+#       1. an operation that maps two elements a and b of the set into a third element of the set, for example c = a + b,
+#       2. an "identity element" e such that e + a = a for any element a, and
+#       3. an inverse -a for every element a, such that a + (-a) = e.
+#
+#     A group is called "Abelian" if a + b = b + a for all a and b, otherwise it is called non-Abelian. 
+#     In general, Abelian groups are simpler to work with than non-Abelian groups.
+#
+#     With this definition we can easily see that the group of integers {0,1,...,11} with addition modulo 12 forms an Abelian group: 
+#     there is an addition operation such that a + b = b + a, the element 0 is the identity element since 0 + a = a for 
+#     all a, and the inverse of element a modulo 12 is 12 - a.
+#
+# The values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The number of periods is then the biggest number that divides all the measurement outcomes, 
+# and the period is the number of integers divided by the number of periods. 
+# In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the largest common divisor as 3, which is the number of periods fitting
+# into 12. From there we can recover the number of periods 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+# ----------------
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+plt.scatter(x, y)
+plt.show()
+
+#####################################################################
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 2` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application this 
+# would be a quantum circuit defined by a sequence of gates.
+
+
+def Oracle(f):
+    """
+    Defines the unitary that implements a function f:{0,..,15} -> {0,..,3}.
+
+    Args:
+      f (func): function to implement
+
+    Returns:
+      ndarray representing the unitary
+    """
+
+    U = np.zeros((2**7, 2**7))
+
+    for x in range(2**4):
+        for f_x in range(2**3):
+            # we know that the initial state has only support on basis states
+            # of the form |x>|0>, and therefore write all necessary information
+            # into the entries of the unitary acting on those states
+            if f_x == 0:
+                i = to_int(to_binary(x, 4) + "0" * 3)
+                j = to_int(to_binary(x, 4) + to_binary(f(x), 3))
+                U[i, j] = 1
+                U[j, i] = 1
+            else:
+                # in all other cases we use trivial entries
+                i = to_int(to_binary(x, 4) + to_binary(f_x, 3))
+                U[i, i] = 1
+
+    # check that this is a unitary
+    assert np.allclose(U @ np.linalg.inv(U), np.eye(2**7))
+
+    return qml.QubitUnitary(U, wires=range(7))
+
+
+#####################################################################
+# Now we're ready to go. Let's implement the famous period finding algorithm. It consists of a
+# quantum routine and a classical postprocessing step. As mentioned, the quantum part is simple: prepare the
+# desired initial state :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum Fourier transform
+# onto the :math:`|x\rangle`-register, and measure in the computational basis. Effectively, this
+# measures in the "Fourier basis", which is where all the magic happens.
+#
+# We only have to get two unique samples, from which we can compute the period of the function. For this 
+# reason we define a device with 2 shots. We also add some snapshots to the circuit that we will look at later.
+
+
+dev = qml.device("default.qubit", wires=7, shots=2)
+
+
+@qml.qnode(dev)
+def circuit():
+    """Circuit to implement the period finding algorithm."""
+
+    for i in range(4):
+        qml.Hadamard(wires=i)
+
+    qml.Snapshot("initial_state")
+
+    Oracle(f)
+
+    qml.Snapshot("loaded_function")
+
+    qml.QFT(wires=range(4))
+
+    qml.Snapshot("fourier_spectrum")
+
+    return qml.sample(wires=range(4))
+
+
+# take two samples from the circuit
+samples = circuit()
+
+#####################################################################
+# The classical post-processing is relatively simple for period finding (whereas 
+# for general hidden subgroup problems it requires some less trivial algebra). 
+# If you are curious about the details, you'll find them in
+# Childs & Dam (2013) [#Childs2010]_, Section IVa.
path: demonstrations/tutorial_period_finding.py
user: login: simidzija
body: van Dam
created_at: 2025-03-24T13:29:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,361 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. 
+#
+# .. admonition:: Abelian group
+#     :class: note
+#
+#     A group is a set of elements that has:
+#       1. an operation that maps two elements a and b of the set into a third element of the set, for example c = a + b,
+#       2. an "identity element" e such that e + a = a for any element a, and
+#       3. an inverse -a for every element a, such that a + (-a) = e.
+#
+#     A group is called "Abelian" if a + b = b + a for all a and b, otherwise it is called non-Abelian. 
+#     In general, Abelian groups are simpler to work with than non-Abelian groups.
+#
+#     With this definition we can easily see that the group of integers {0,1,...,11} with addition modulo 12 forms an Abelian group: 
+#     there is an addition operation such that a + b = b + a, the element 0 is the identity element since 0 + a = a for 
+#     all a, and the inverse of element a modulo 12 is 12 - a.
+#
+# The values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The number of periods is then the biggest number that divides all the measurement outcomes, 
+# and the period is the number of integers divided by the number of periods. 
+# In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the largest common divisor as 3, which is the number of periods fitting
+# into 12. From there we can recover the number of periods 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+# ----------------
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+plt.scatter(x, y)
+plt.show()
+
+#####################################################################
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 2` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application this 
+# would be a quantum circuit defined by a sequence of gates.
+
+
+def Oracle(f):
+    """
+    Defines the unitary that implements a function f:{0,..,15} -> {0,..,3}.
+
+    Args:
+      f (func): function to implement
+
+    Returns:
+      ndarray representing the unitary
+    """
+
+    U = np.zeros((2**7, 2**7))
+
+    for x in range(2**4):
+        for f_x in range(2**3):
+            # we know that the initial state has only support on basis states
+            # of the form |x>|0>, and therefore write all necessary information
+            # into the entries of the unitary acting on those states
+            if f_x == 0:
+                i = to_int(to_binary(x, 4) + "0" * 3)
+                j = to_int(to_binary(x, 4) + to_binary(f(x), 3))
+                U[i, j] = 1
+                U[j, i] = 1
+            else:
+                # in all other cases we use trivial entries
+                i = to_int(to_binary(x, 4) + to_binary(f_x, 3))
+                U[i, i] = 1
+
+    # check that this is a unitary
+    assert np.allclose(U @ np.linalg.inv(U), np.eye(2**7))
+
+    return qml.QubitUnitary(U, wires=range(7))
+
+
+#####################################################################
+# Now we're ready to go. Let's implement the famous period finding algorithm. It consists of a
+# quantum routine and a classical postprocessing step. As mentioned, the quantum part is simple: prepare the
+# desired initial state :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum Fourier transform
+# onto the :math:`|x\rangle`-register, and measure in the computational basis. Effectively, this
+# measures in the "Fourier basis", which is where all the magic happens.
+#
+# We only have to get two unique samples, from which we can compute the period of the function. For this 
+# reason we define a device with 2 shots. We also add some snapshots to the circuit that we will look at later.
+
+
+dev = qml.device("default.qubit", wires=7, shots=2)
+
+
+@qml.qnode(dev)
+def circuit():
+    """Circuit to implement the period finding algorithm."""
+
+    for i in range(4):
+        qml.Hadamard(wires=i)
+
+    qml.Snapshot("initial_state")
+
+    Oracle(f)
+
+    qml.Snapshot("loaded_function")
+
+    qml.QFT(wires=range(4))
+
+    qml.Snapshot("fourier_spectrum")
+
+    return qml.sample(wires=range(4))
+
+
+# take two samples from the circuit
+samples = circuit()
+
+#####################################################################
+# The classical post-processing is relatively simple for period finding (whereas 
+# for general hidden subgroup problems it requires some less trivial algebra). 
+# If you are curious about the details, you'll find them in
+# Childs & Dam (2013) [#Childs2010]_, Section IVa.
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: ```suggestion
# Childs & van Dam (2013) [#Childs2010]_, Section IVa.
```
created_at: 2025-03-24T14:13:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,361 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. 
+#
+# .. admonition:: Abelian group
+#     :class: note
+#
+#     A group is a set of elements that has:
+#       1. an operation that maps two elements a and b of the set into a third element of the set, for example c = a + b,
+#       2. an "identity element" e such that e + a = a for any element a, and
+#       3. an inverse -a for every element a, such that a + (-a) = e.
+#
+#     A group is called "Abelian" if a + b = b + a for all a and b, otherwise it is called non-Abelian. 
+#     In general, Abelian groups are simpler to work with than non-Abelian groups.
+#
+#     With this definition we can easily see that the group of integers {0,1,...,11} with addition modulo 12 forms an Abelian group: 
+#     there is an addition operation such that a + b = b + a, the element 0 is the identity element since 0 + a = a for 
+#     all a, and the inverse of element a modulo 12 is 12 - a.
+#
+# The values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The number of periods is then the biggest number that divides all the measurement outcomes, 
+# and the period is the number of integers divided by the number of periods. 
+# In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the largest common divisor as 3, which is the number of periods fitting
+# into 12. From there we can recover the number of periods 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+# ----------------
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+plt.scatter(x, y)
+plt.show()
+
+#####################################################################
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 2` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application this 
+# would be a quantum circuit defined by a sequence of gates.
+
+
+def Oracle(f):
+    """
+    Defines the unitary that implements a function f:{0,..,15} -> {0,..,3}.
+
+    Args:
+      f (func): function to implement
+
+    Returns:
+      ndarray representing the unitary
+    """
+
+    U = np.zeros((2**7, 2**7))
+
+    for x in range(2**4):
+        for f_x in range(2**3):
+            # we know that the initial state has only support on basis states
+            # of the form |x>|0>, and therefore write all necessary information
+            # into the entries of the unitary acting on those states
+            if f_x == 0:
+                i = to_int(to_binary(x, 4) + "0" * 3)
+                j = to_int(to_binary(x, 4) + to_binary(f(x), 3))
+                U[i, j] = 1
+                U[j, i] = 1
+            else:
+                # in all other cases we use trivial entries
+                i = to_int(to_binary(x, 4) + to_binary(f_x, 3))
+                U[i, i] = 1
+
+    # check that this is a unitary
+    assert np.allclose(U @ np.linalg.inv(U), np.eye(2**7))
+
+    return qml.QubitUnitary(U, wires=range(7))
+
+
+#####################################################################
+# Now we're ready to go. Let's implement the famous period finding algorithm. It consists of a
+# quantum routine and a classical postprocessing step. As mentioned, the quantum part is simple: prepare the
+# desired initial state :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum Fourier transform
+# onto the :math:`|x\rangle`-register, and measure in the computational basis. Effectively, this
+# measures in the "Fourier basis", which is where all the magic happens.
+#
+# We only have to get two unique samples, from which we can compute the period of the function. For this 
+# reason we define a device with 2 shots. We also add some snapshots to the circuit that we will look at later.
+
+
+dev = qml.device("default.qubit", wires=7, shots=2)
+
+
+@qml.qnode(dev)
+def circuit():
+    """Circuit to implement the period finding algorithm."""
+
+    for i in range(4):
+        qml.Hadamard(wires=i)
+
+    qml.Snapshot("initial_state")
+
+    Oracle(f)
+
+    qml.Snapshot("loaded_function")
+
+    qml.QFT(wires=range(4))
+
+    qml.Snapshot("fourier_spectrum")
+
+    return qml.sample(wires=range(4))
+
+
+# take two samples from the circuit
+samples = circuit()
+
+#####################################################################
+# The classical post-processing is relatively simple for period finding (whereas 
+# for general hidden subgroup problems it requires some less trivial algebra). 
+# If you are curious about the details, you'll find them in
+# Childs & Dam (2013) [#Childs2010]_, Section IVa.
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: Good catch!

created_at: 2025-03-24T14:13:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,364 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: ```suggestion
While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
```
created_at: 2025-04-02T06:53:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,364 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#    :target: javascript:void(0);
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. 
+#
+# .. admonition:: Abelian group
+#     :class: note
+#
+#     A group is a set of elements that has:
+#       1. an operation that maps two elements a and b of the set into a third element of the set, for example c = a + b,
+#       2. an "identity element" e such that e + a = a for any element a, and
+#       3. an inverse -a for every element a, such that a + (-a) = e.
+#
+#     A group is called "Abelian" if a + b = b + a for all a and b, otherwise it is called non-Abelian. 
+#     In general, Abelian groups are simpler to work with than non-Abelian groups.
+#
+#     With this definition we can easily see that the group of integers {0,1,...,11} with addition modulo 12 forms an Abelian group: 
+#     there is an addition operation such that a + b = b + a, the element 0 is the identity element since 0 + a = a for 
+#     all a, and the inverse of element a modulo 12 is 12 - a.
+#
+# The values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#    :target: javascript:void(0);
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The number of periods is then the biggest number that divides all the measurement outcomes, 
+# and the period is the number of integers divided by the number of periods. 
+# In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the largest common divisor as 3, which is the number of periods fitting
+# into 12. From there we can recover the number of periods 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+# ----------------
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+plt.scatter(x, y)
+plt.show()
+
+#####################################################################
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 2` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application this 
+# would be a quantum circuit defined by a sequence of gates.
+
+
+def Oracle(f):
+    """
+    Defines the unitary that implements a function f:{0,..,15} -> {0,..,3}.
+
+    Args:
+      f (func): function to implement
+
+    Returns:
+      ndarray representing the unitary
+    """
+
+    U = np.zeros((2**7, 2**7))
+
+    for x in range(2**4):
+        for f_x in range(2**3):
+            # we know that the initial state has only support on basis states
+            # of the form |x>|0>, and therefore write all necessary information
+            # into the entries of the unitary acting on those states
+            if f_x == 0:
+                i = to_int(to_binary(x, 4) + "0" * 3)
+                j = to_int(to_binary(x, 4) + to_binary(f(x), 3))
+                U[i, j] = 1
+                U[j, i] = 1
+            else:
+                # in all other cases we use trivial entries
+                i = to_int(to_binary(x, 4) + to_binary(f_x, 3))
+                U[i, i] = 1
+
+    # check that this is a unitary
+    assert np.allclose(U @ np.linalg.inv(U), np.eye(2**7))
+
+    return qml.QubitUnitary(U, wires=range(7))
+
+
+#####################################################################
+# Now we're ready to go. Let's implement the famous period finding algorithm. It consists of a
+# quantum routine and a classical postprocessing step. As mentioned, the quantum part is simple: prepare the
+# desired initial state :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum Fourier transform
+# onto the :math:`|x\rangle`-register, and measure in the computational basis. Effectively, this
+# measures in the "Fourier basis", which is where all the magic happens.
+#
+# We only have to get two unique samples, from which we can compute the period of the function. For this 
+# reason we define a device with 2 shots. We also add some snapshots to the circuit that we will look at later.
+
+
+dev = qml.device("default.qubit", wires=7, shots=2)
+
+
+@qml.qnode(dev)
+def circuit():
+    """Circuit to implement the period finding algorithm."""
+
+    for i in range(4):
+        qml.Hadamard(wires=i)
+
+    qml.Snapshot("initial_state")
+
+    Oracle(f)
+
+    qml.Snapshot("loaded_function")
+
+    qml.QFT(wires=range(4))
+
+    qml.Snapshot("fourier_spectrum")
+
+    return qml.sample(wires=range(4))
+
+
+# take two samples from the circuit
+samples = circuit()
+
+#####################################################################
+# The classical post-processing is relatively simple for period finding (whereas 
+# for general hidden subgroup problems it requires some less trivial algebra). 
+# If you are curious about the details, you'll find them in
+# Childs & van Dam (2013) [#Childs2010]_, Section IVa.
+
+from fractions import Fraction
+from math import lcm
+
+# convert the bistrings to integers
+sample1_int = to_int("".join(str(s) for s in samples[0]))
+sample2_int = to_int("".join(str(s) for s in samples[1]))
+
+# get the denominator of the fraction representation
+denominator1 = Fraction(sample1_int / (2**4)).denominator
+denominator2 = Fraction(sample2_int / (2**4)).denominator
+
+# get the least common multiple
+result = lcm(denominator1, denominator2)
+
+print(f"Hidden period: {result}")
+
+#####################################################################
+# Yay, we've found the hidden period! Now, of course this is only impressive if we increase the 
+# number of qubits. We can find the hidden period of a function defined on :math:`2^n` values in time
+# :math:`O(n \mathrm{log}(n))`.
+
+
+#####################################################################
+# A peep at the states involved
+# -----------------------------
+#
+# Somehow the quantum Fourier transform exposed the information we needed. Let's have a closer
+# look at the states that were prepared, by making use of the snapshots we recorded during the
+# circuit simulation.
+
+dev = qml.device("default.qubit", wires=7, shots=1)
+qnode = qml.QNode(circuit, dev)
+intermediate_states = qml.snapshots(circuit)()
+
+#####################################################################
+# We can plot them as discrete functions, where the size of a point indicates the absolute value
+# of the amplitude.
+
+import matplotlib.patches as mpatches
+
+x = [i for i in range(2**4) for _ in range(2**3)]
+y = list(range(2**3)) * (2**4)
+
+states_to_plot = ["initial_state", "loaded_function", "fourier_spectrum"]
+
+fig, axes = plt.subplots(3, 1, sharex=True, figsize=(7, 6))
+
+for state, ax in zip(states_to_plot, axes):
+
+    real = np.real(intermediate_states[state])
+    imag = np.imag(intermediate_states[state])
+
+    # scale the points by the absolute value of the amplitude
+    sizes_real = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_real.append(200 * np.abs(real[idx]))
+
+    sizes_imag = []
+    for x_, y_ in zip(x, y):
+        idx = to_int(to_binary(x_, 4) + to_binary(y_, 3))
+        sizes_imag.append(200 * np.abs(imag[idx]))
+
+    ax.scatter(np.array(x), y, s=sizes_real, c="green", alpha=0.5)
+    ax.scatter(np.array(x), y, s=sizes_imag, c="pink", alpha=0.5)
+    ax.set_title(state)
+    ax.set_ylabel("f(x)")
+
+plt.xlabel("x")
+green = mpatches.Patch(color="green", label="abs(real part)")
+pink = mpatches.Patch(color="pink", label="abs(imaginary part)")
+plt.legend(handles=[green, pink])
+plt.tight_layout()
+plt.show()
+
+#####################################################################
+# First of all, we see that the oracle really prepared a quantum state that we can interpret as 
+# our periodic function ``f``.
+#
+# Furthermore, the state representing the Fourier spectrum looks actually quite interesting. But
+# the important feature of this state is also clearly visible: Amplitudes concentrate in :math:`x`
+# values that are multiples of 2. And 2 was exactly the amount of periods of 8 that fit into 16.
+
+#####################################################################
+# What is the "Abelian structure" exploited by the quantum Fourier transform?
+# ----------------------------------------------------------------------------
+#
+# Why does the Fourier transform prepare a state with such a convenient structure? Let us have a
+# look how it acts on a superposition of inputs :math:`x` for which :math:`f(x)` has the same value, such as
+# :math:`\frac{1}{\sqrt{2}} (|2\rangle + |10\rangle)` (where we translated bitstrings to integers for
+# better readability). The quantum Fourier transform prepares a new state whose amplitudes are
+# proportional to
+#
+# .. math::
+#
+#     \sum_k \left(e^{\frac{2 \pi i 2 k}{12}} + e^{\frac{2 \pi i 10 k}{12}} \right)  |k \rangle.
+#
+# In the exponent you find the values 2 and 10, as well as the size of the group, 12.
+# Somewhat magically, for some :math:`|k \rangle`, all exponential functions in the sum evaluate to :math:`1`, 
+# while for all others, the functions cancel each other out and evaluate exactly to zero.
+
+for k in range(12):
+    res = np.exp(2 * np.pi * 1j * 2 * k / 16) + np.exp(2 * np.pi * 1j * 10 * k / 16)
+    print(f"k={k} --- {np.round(res, 13)}")
+
+#####################################################################
+# This pattern is the same for whichever set of :math:`x` with the same value :math:`f(x)` 
+# (in other words, which "coset") we picked. It is also
+# true for whichever period we encoded, function we chose, and even which (Abelian) group we
+# started with.
+#
+# The "magic" interference, of course, would not surprise a group theorist; it is inherent to the
+# structure of the group. The functions :math:`e^{\frac{2 \pi i x k}{12}}`, which we are so used to
+# see in quantum theory, are nothing but the *characters* of a group: functions that take group
+# elements :math:`x` to complex numbers, and -- a little like eigenvalues in linear algebra -- capture
+# the essentials of the group. The destructive interference we see in the quantum Fourier
+# transform is nothing other than the orthogonality theorem for these characters.
+#
+# It is strangely beautiful how quantum interference leverages the structure of Abelian groups in
+# the quantum Fourier transform. So far this has only found application for rather abstract
+# problems. Even though cryptography, impacted by Shor's algorithm, is what one might consider a
+# "real-world" application, it is an outlier, since problems in cryptography are artificially
+# *constructed* from abstract mathematical theory. A fascinating question is whether "real"
+# real-world applications, like material science or data analysis, could benefit from this
+# remarkable confluence of group structure and quantum theory.
+#
+#
+# References
+# ------------
+#
+# .. [#Childs2010] 
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: ```suggestion
# .. [#Aaronson22]_ Scott Aaronson, "How Much Structure Is Needed for #    Huge Quantum Speedups?" <https://arxiv.org/pdf/2209.06930>`_
# 
# .. [#Childs2010] 
```
created_at: 2025-04-02T06:54:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,366 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: @CatalinaAlbornoz getting back to your generous offer to add captions in the right format, we could have this one here: 

"Aaronson's 'hierarchy  of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)"  
created_at: 2025-04-02T06:56:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,366 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#    :target: javascript:void(0);
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: @CatalinaAlbornoz unfortunately I used a very specific font for the figure that would take a while to dig out. Would it be ok to explain the axes in the caption?

"Example of a discrete periodic function f(x) over the integers x = 0,...,11. The function only takes the same value when moving exactly 4 integers on the x-axis."
created_at: 2025-04-02T07:01:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,366 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 70%
+#    :target: javascript:void(0);
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. 
+#
+# .. admonition:: Abelian group
+#     :class: note
+#
+#     A group is a set of elements that has:
+#       1. an operation that maps two elements a and b of the set into a third element of the set, for example c = a + b,
+#       2. an "identity element" e such that e + a = a for any element a, and
+#       3. an inverse -a for every element a, such that a + (-a) = e.
+#
+#     A group is called "Abelian" if a + b = b + a for all a and b, otherwise it is called non-Abelian. 
+#     In general, Abelian groups are simpler to work with than non-Abelian groups.
+#
+#     With this definition we can easily see that the group of integers {0,1,...,11} with addition modulo 12 forms an Abelian group: 
+#     there is an addition operation such that a + b = b + a, the element 0 is the identity element since 0 + a = a for 
+#     all a, and the inverse of element a modulo 12 is 12 - a.
+#
+# The values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#    :target: javascript:void(0);
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: Caption: "Periodic function from above, from the perspective of group theory. "
created_at: 2025-04-02T07:02:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+    Figure 1. Aaronson's 'hierarchy of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
path: demonstrations/tutorial_period_finding.py
body: ```suggestion
However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function—something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
```
created_at: 2025-04-04T15:31:24Z
author_association: NONE
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+    Figure 1. Aaronson's 'hierarchy of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
path: demonstrations/tutorial_period_finding.py
body: ```suggestion
# The period finding problem
```
created_at: 2025-04-04T15:33:07Z
author_association: NONE
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+    Figure 1. Aaronson's 'hierarchy of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
path: demonstrations/tutorial_period_finding.py
body: Reason for this: subsections need to be more specific so that people find this in Google. It's better if they are called something that people would search for, as opposed to something generic.
created_at: 2025-04-04T15:34:07Z
author_association: NONE
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+    Figure 1. Aaronson's 'hierarchy of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 90%
+#    :target: javascript:void(0);
+#    :figwidth: 80%
+#    
+#    Figure 2. Example of a discrete periodic function f(x) over the integers x = 0,...,11. 
+#    The function only takes the same value when moving exactly 4 integers on the x-axis.
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
path: demonstrations/tutorial_period_finding.py
body: ```suggestion
# Importantly, we assume that we have *black-box access* to that function. For a python coder, this 
```
created_at: 2025-04-04T15:34:46Z
author_association: NONE
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+    Figure 1. Aaronson's 'hierarchy of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 90%
+#    :target: javascript:void(0);
+#    :figwidth: 80%
+#    
+#    Figure 2. Example of a discrete periodic function f(x) over the integers x = 0,...,11. 
+#    The function only takes the same value when moving exactly 4 integers on the x-axis.
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. 
+#
+# .. admonition:: Abelian group
+#     :class: note
+#
+#     A group is a set of elements that has:
+#       1. an operation that maps two elements a and b of the set into a third element of the set, for example c = a + b,
+#       2. an "identity element" e such that e + a = a for any element a, and
+#       3. an inverse -a for every element a, such that a + (-a) = e.
+#
+#     A group is called "Abelian" if a + b = b + a for all a and b, otherwise it is called non-Abelian. 
+#     In general, Abelian groups are simpler to work with than non-Abelian groups.
+#
+#     With this definition we can easily see that the group of integers {0,1,...,11} with addition modulo 12 forms an Abelian group: 
+#     there is an addition operation such that a + b = b + a, the element 0 is the identity element since 0 + a = a for 
+#     all a, and the inverse of element a modulo 12 is 12 - a.
+#
+# The values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#    :target: javascript:void(0);
+#    
+#    Figure 3. Periodic function from above, from the perspective of group theory.
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
path: demonstrations/tutorial_period_finding.py
body: ```suggestion
# Fourier transform on the first register, and measure. We then need to do a bit of post-processing:
# The state we measure, written as an integer, is a multiple of the number of periods
```
created_at: 2025-04-04T15:39:41Z
author_association: NONE
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+    Figure 1. Aaronson's 'hierarchy of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 90%
+#    :target: javascript:void(0);
+#    :figwidth: 80%
+#    
+#    Figure 2. Example of a discrete periodic function f(x) over the integers x = 0,...,11. 
+#    The function only takes the same value when moving exactly 4 integers on the x-axis.
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. 
+#
+# .. admonition:: Abelian group
+#     :class: note
+#
+#     A group is a set of elements that has:
+#       1. an operation that maps two elements a and b of the set into a third element of the set, for example c = a + b,
+#       2. an "identity element" e such that e + a = a for any element a, and
+#       3. an inverse -a for every element a, such that a + (-a) = e.
+#
+#     A group is called "Abelian" if a + b = b + a for all a and b, otherwise it is called non-Abelian. 
+#     In general, Abelian groups are simpler to work with than non-Abelian groups.
+#
+#     With this definition we can easily see that the group of integers {0,1,...,11} with addition modulo 12 forms an Abelian group: 
+#     there is an addition operation such that a + b = b + a, the element 0 is the identity element since 0 + a = a for 
+#     all a, and the inverse of element a modulo 12 is 12 - a.
+#
+# The values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#    :target: javascript:void(0);
+#    
+#    Figure 3. Periodic function from above, from the perspective of group theory.
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The number of periods is then the biggest number that divides all the measurement outcomes, 
+# and the period is the number of integers divided by the number of periods. 
+# In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the largest common divisor as 3, which is the number of periods fitting
+# into 12. From there we can recover the number of periods 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+# ----------------
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+fig = plt.figure()
+ax = fig.add_axes((0.1, 0.2, 0.8, 0.7))
+ax.scatter(x, y)
+ax.set_title("Periodic function")
+ax.set_ylabel("f(x)")
+ax.set_xlabel("x")
+fig.text(0.5, 0.05, "Figure 4. Periodic function f(x)", horizontalalignment='center')
+plt.show()
+
+#####################################################################
+# 
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 2` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application this 
+# would be a quantum circuit defined by a sequence of gates.
+
+
+def Oracle(f):
+    """
+    Defines the unitary that implements a function f:{0,..,15} -> {0,..,3}.
+
+    Args:
+      f (func): function to implement
+
+    Returns:
+      ndarray representing the unitary
+    """
+
+    U = np.zeros((2**7, 2**7))
+
+    for x in range(2**4):
+        for f_x in range(2**3):
+            # we know that the initial state has only support on basis states
+            # of the form |x>|0>, and therefore write all necessary information
+            # into the entries of the unitary acting on those states
+            if f_x == 0:
+                i = to_int(to_binary(x, 4) + "0" * 3)
+                j = to_int(to_binary(x, 4) + to_binary(f(x), 3))
+                U[i, j] = 1
+                U[j, i] = 1
+            else:
+                # in all other cases we use trivial entries
+                i = to_int(to_binary(x, 4) + to_binary(f_x, 3))
+                U[i, i] = 1
+
+    # check that this is a unitary
+    assert np.allclose(U @ np.linalg.inv(U), np.eye(2**7))
+
+    return qml.QubitUnitary(U, wires=range(7))
+
+
+#####################################################################
+# Now we're ready to go. Let's implement the famous period finding algorithm. It consists of a
+# quantum routine and a classical postprocessing step. As mentioned, the quantum part is simple: prepare the
path: demonstrations/tutorial_period_finding.py
body: ```suggestion
# quantum routine and a classical post-processing step. As mentioned, the quantum part is simple: prepare the
```
created_at: 2025-04-04T15:43:08Z
author_association: NONE
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+    Figure 1. Aaronson's 'hierarchy of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 90%
+#    :target: javascript:void(0);
+#    :figwidth: 80%
+#    
+#    Figure 2. Example of a discrete periodic function f(x) over the integers x = 0,...,11. 
+#    The function only takes the same value when moving exactly 4 integers on the x-axis.
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. 
+#
+# .. admonition:: Abelian group
+#     :class: note
+#
+#     A group is a set of elements that has:
+#       1. an operation that maps two elements a and b of the set into a third element of the set, for example c = a + b,
+#       2. an "identity element" e such that e + a = a for any element a, and
+#       3. an inverse -a for every element a, such that a + (-a) = e.
+#
+#     A group is called "Abelian" if a + b = b + a for all a and b, otherwise it is called non-Abelian. 
+#     In general, Abelian groups are simpler to work with than non-Abelian groups.
+#
+#     With this definition we can easily see that the group of integers {0,1,...,11} with addition modulo 12 forms an Abelian group: 
+#     there is an addition operation such that a + b = b + a, the element 0 is the identity element since 0 + a = a for 
+#     all a, and the inverse of element a modulo 12 is 12 - a.
+#
+# The values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#    :target: javascript:void(0);
+#    
+#    Figure 3. Periodic function from above, from the perspective of group theory.
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The number of periods is then the biggest number that divides all the measurement outcomes, 
+# and the period is the number of integers divided by the number of periods. 
+# In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the largest common divisor as 3, which is the number of periods fitting
+# into 12. From there we can recover the number of periods 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
path: demonstrations/tutorial_period_finding.py
body: ```suggestion
# Implementation of period finding in PennyLane
```
created_at: 2025-04-04T17:10:19Z
author_association: NONE
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+    Figure 1. Aaronson's 'hierarchy of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 90%
+#    :target: javascript:void(0);
+#    :figwidth: 80%
+#    
+#    Figure 2. Example of a discrete periodic function f(x) over the integers x = 0,...,11. 
+#    The function only takes the same value when moving exactly 4 integers on the x-axis.
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. 
+#
+# .. admonition:: Abelian group
+#     :class: note
+#
+#     A group is a set of elements that has:
+#       1. an operation that maps two elements a and b of the set into a third element of the set, for example c = a + b,
+#       2. an "identity element" e such that e + a = a for any element a, and
+#       3. an inverse -a for every element a, such that a + (-a) = e.
+#
+#     A group is called "Abelian" if a + b = b + a for all a and b, otherwise it is called non-Abelian. 
+#     In general, Abelian groups are simpler to work with than non-Abelian groups.
+#
+#     With this definition we can easily see that the group of integers {0,1,...,11} with addition modulo 12 forms an Abelian group: 
+#     there is an addition operation such that a + b = b + a, the element 0 is the identity element since 0 + a = a for 
+#     all a, and the inverse of element a modulo 12 is 12 - a.
+#
+# The values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#    :target: javascript:void(0);
+#    
+#    Figure 3. Periodic function from above, from the perspective of group theory.
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The number of periods is then the biggest number that divides all the measurement outcomes, 
+# and the period is the number of integers divided by the number of periods. 
+# In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the largest common divisor as 3, which is the number of periods fitting
+# into 12. From there we can recover the number of periods 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+# ----------------
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+fig = plt.figure()
+ax = fig.add_axes((0.1, 0.2, 0.8, 0.7))
+ax.scatter(x, y)
+ax.set_title("Periodic function")
+ax.set_ylabel("f(x)")
+ax.set_xlabel("x")
+fig.text(0.5, 0.05, "Figure 4. Periodic function f(x)", horizontalalignment='center')
+plt.show()
+
+#####################################################################
+# 
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 2` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application this 
path: demonstrations/tutorial_period_finding.py
body: ```suggestion
# together by defining a matrix that does the job. Of course, in a real application, this 
```
created_at: 2025-04-04T17:10:45Z
author_association: NONE
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+    Figure 1. Aaronson's 'hierarchy of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 90%
+#    :target: javascript:void(0);
+#    :figwidth: 80%
+#    
+#    Figure 2. Example of a discrete periodic function f(x) over the integers x = 0,...,11. 
+#    The function only takes the same value when moving exactly 4 integers on the x-axis.
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. 
+#
+# .. admonition:: Abelian group
+#     :class: note
+#
+#     A group is a set of elements that has:
+#       1. an operation that maps two elements a and b of the set into a third element of the set, for example c = a + b,
+#       2. an "identity element" e such that e + a = a for any element a, and
+#       3. an inverse -a for every element a, such that a + (-a) = e.
+#
+#     A group is called "Abelian" if a + b = b + a for all a and b, otherwise it is called non-Abelian. 
+#     In general, Abelian groups are simpler to work with than non-Abelian groups.
+#
+#     With this definition we can easily see that the group of integers {0,1,...,11} with addition modulo 12 forms an Abelian group: 
+#     there is an addition operation such that a + b = b + a, the element 0 is the identity element since 0 + a = a for 
+#     all a, and the inverse of element a modulo 12 is 12 - a.
+#
+# The values {0,4,8} form a *subgroup* that is "generated" by the period 4. Finding the period
+# means to find the subgroup. The function is said to "hide" the subgroup, since it has the same
+# value on all its elements, effectively labeling them. It is also constant on the *cosets* of
+# the subgroups, which are shifted sets of the form {0+s, 4+s, 8+s} (where s is an
+# element of the group, or a number between 0 and 11). 
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function_groups.png
+#    :align: center
+#    :width: 70%
+#    :target: javascript:void(0);
+#    
+#    Figure 3. Periodic function from above, from the perspective of group theory.
+#
+# This jargon hopefully does not scare you, but should illustrate that
+# period finding --- and the algorithm we will have a look at here --- can be generalised to other groups and
+# subgroups. It can even be efficiently run for some instances of non-Abelian groups.
+#
+# The quantum algorithm to find the period of the function above is really simple. Encode the
+# function into a quantum state of the form :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum
+# Fourier transform on the first register, and measure. We then need to do a bit of post-
+# processing: The state we measure, written as an integer, is a multiple of the number of periods
+# that "fit" into the x-domain. The number of periods is then the biggest number that divides all the measurement outcomes, 
+# and the period is the number of integers divided by the number of periods. 
+# In the example above, the quantum algorithm would return random bistrings that can be
+# interpreted as integers {0, 3, 6, 9}. By sampling only two distinct values, say 6 and
+# 9, we can determine the largest common divisor as 3, which is the number of periods fitting
+# into 12. From there we can recover the number of periods 12/3 = 4.
+#
+# We'll now implement this recipe, but use a more convenient cyclic group of 16 elements
+# {0,..,15}, which can be exactly represented by 4 qubits.
+
+
+#####################################################################
+# Implementation
+# ----------------
+
+import pennylane as qml
+import numpy as np
+import matplotlib.pyplot as plt
+
+#####################################################################
+# First we need to define the periodic function :math:`f(x)`. As mentioned, this function is considered
+# unknown in principle, but we can call it on a classical computer, and --- theoretically, by turning the
+# classical logical circuit into a reversible quantum circuit --- we assume we can also call it on a quantum computer. The call
+# on a quantum computer can be implemented in parallel for a superposition of inputs, which is
+# part of the trick of this algorithm.
+
+
+def f(x):
+    """
+    Function whose period we want to find.
+
+    Args:
+      x (int): integer in {0,..,15}
+
+    Returns:
+      integer in {0,..,3}
+    """
+    return x % 8
+
+
+# let's plot this!
+x = range(16)
+y = [f(x_) for x_ in x]
+fig = plt.figure()
+ax = fig.add_axes((0.1, 0.2, 0.8, 0.7))
+ax.scatter(x, y)
+ax.set_title("Periodic function")
+ax.set_ylabel("f(x)")
+ax.set_xlabel("x")
+fig.text(0.5, 0.05, "Figure 4. Periodic function f(x)", horizontalalignment='center')
+plt.show()
+
+#####################################################################
+# 
+# We will represent the :math:`x` and :math:`f(x)` values of this function as computational basis states
+# :math:`| x \rangle | f(x) \rangle`. The amplitudes belonging to that state can be interpreted as a
+# "weight" for a specific function value. 
+# To give an example, the point :math:`f(2) = 2` can be expressed by preparing a state that has a
+# nonzero uniform amplitude at :math:`| 0010 \rangle | 11 \rangle`, but zero amplitudes at the states
+# :math:`| 0010 \rangle | 00 \rangle`, :math:`| 0010 \rangle | 01 \rangle`, :math:`| 0010 \rangle | 10 \rangle`.
+#
+# Since we move between integers and computational basis states, we need two utility conversion functions.
+
+
+def to_int(binary):
+    return int(binary, 2)
+
+
+def to_binary(integer, n):
+    return format(integer, "b").zfill(n)
+
+
+#####################################################################
+# Now we need an oracle that implements the function ``f`` in "quantum parallel". Applied to a
+# superposition of inputs, :math:`\sum_x |x \rangle |0 \rangle`, this unitary prepares the state
+# :math:`\sum_x |x \rangle |f(x) \rangle`. There are many such unitaries and here we'll somewhat hack it 
+# together by defining a matrix that does the job. Of course, in a real application this 
+# would be a quantum circuit defined by a sequence of gates.
+
+
+def Oracle(f):
+    """
+    Defines the unitary that implements a function f:{0,..,15} -> {0,..,3}.
+
+    Args:
+      f (func): function to implement
+
+    Returns:
+      ndarray representing the unitary
+    """
+
+    U = np.zeros((2**7, 2**7))
+
+    for x in range(2**4):
+        for f_x in range(2**3):
+            # we know that the initial state has only support on basis states
+            # of the form |x>|0>, and therefore write all necessary information
+            # into the entries of the unitary acting on those states
+            if f_x == 0:
+                i = to_int(to_binary(x, 4) + "0" * 3)
+                j = to_int(to_binary(x, 4) + to_binary(f(x), 3))
+                U[i, j] = 1
+                U[j, i] = 1
+            else:
+                # in all other cases we use trivial entries
+                i = to_int(to_binary(x, 4) + to_binary(f_x, 3))
+                U[i, i] = 1
+
+    # check that this is a unitary
+    assert np.allclose(U @ np.linalg.inv(U), np.eye(2**7))
+
+    return qml.QubitUnitary(U, wires=range(7))
+
+
+#####################################################################
+# Now we're ready to go. Let's implement the famous period finding algorithm. It consists of a
+# quantum routine and a classical postprocessing step. As mentioned, the quantum part is simple: prepare the
+# desired initial state :math:`\sum_x |x \rangle |f(x) \rangle`, apply the quantum Fourier transform
+# onto the :math:`|x\rangle`-register, and measure in the computational basis. Effectively, this
+# measures in the "Fourier basis", which is where all the magic happens.
+#
+# We only have to get two unique samples, from which we can compute the period of the function. For this 
+# reason we define a device with 2 shots. We also add some snapshots to the circuit that we will look at later.
+
+
+dev = qml.device("default.qubit", wires=7, shots=2)
+
+
+@qml.qnode(dev)
+def circuit():
+    """Circuit to implement the period finding algorithm."""
+
+    for i in range(4):
+        qml.Hadamard(wires=i)
+
+    qml.Snapshot("initial_state")
+
+    Oracle(f)
+
+    qml.Snapshot("loaded_function")
+
+    qml.QFT(wires=range(4))
+
+    qml.Snapshot("fourier_spectrum")
+
+    return qml.sample(wires=range(4))
+
+
+# take two samples from the circuit
+samples = circuit()
+
+#####################################################################
+# The classical post-processing is relatively simple for period finding (whereas 
+# for general hidden subgroup problems it requires some less trivial algebra). 
+# If you are curious about the details, you'll find them in
+# Childs & van Dam (2013) [#Childs2010]_, Section IVa.
+
+from fractions import Fraction
+from math import lcm
+
+# convert the bistrings to integers
+sample1_int = to_int("".join(str(s) for s in samples[0]))
+sample2_int = to_int("".join(str(s) for s in samples[1]))
+
+# get the denominator of the fraction representation
+denominator1 = Fraction(sample1_int / (2**4)).denominator
+denominator2 = Fraction(sample2_int / (2**4)).denominator
+
+# get the least common multiple
+result = lcm(denominator1, denominator2)
+
+print(f"Hidden period: {result}")
+
+#####################################################################
+# Yay, we've found the hidden period! Now, of course this is only impressive if we increase the 
+# number of qubits. We can find the hidden period of a function defined on :math:`2^n` values in time
+# :math:`O(n \mathrm{log}(n))`.
+
+
+#####################################################################
+# A peep at the states involved
+# -----------------------------
+#
+# Somehow the quantum Fourier transform exposed the information we needed. Let's have a closer
+# look at the states that were prepared, by making use of the snapshots we recorded during the
path: demonstrations/tutorial_period_finding.py
body: ```suggestion
# look at the states that were prepared by making use of the snapshots we recorded during the
```
created_at: 2025-04-04T17:11:59Z
author_association: NONE
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+    Figure 1. Aaronson's 'hierarchy of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function - something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# Problem statement
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: Ah, very good to know, thanks!
created_at: 2025-04-07T06:55:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: ```suggestion
While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
```
created_at: 2025-04-07T07:49:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" [#Aaronson22]_ presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+    Figure 1. Aaronson's 'hierarchy of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function—something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
+"""
+
+#####################################################################
+# The period finding problem
+# -------------------
+#
+# Consider a function :math:`f(x)` that maps from integers, say the numbers between 0 and 11, to
+# some other domain, such as the numbers between 0 and 3. The function is guaranteed to have a
+# periodic structure, which means that it repeats after a certain number of integers. We need a
+# further technical requirement, which is that the function does not have the same value within a
+# period. Here is an example of such a function with a period of 4:
+#
+# .. figure:: ../_static/demonstration_assets/period_finding/periodic_function.png
+#    :align: center
+#    :width: 90%
+#    :target: javascript:void(0);
+#    :figwidth: 80%
+#    
+#    Figure 2. Example of a discrete periodic function f(x) over the integers x = 0,...,11. 
+#    The function only takes the same value when moving exactly 4 integers on the x-axis.
+#
+# Importantly, we assume that we have *black-box access* to that function. For a python coder this 
+# abstract technical term is actually quite intuitive: imagine some part of your code returns a python function ``f`` 
+# that you can call by using integers :math:`x \in \{0,..,11\}` as arguments, like ``f(2)``. However, 
+# you have no knowledge of the definition ``def f(x)`` --- your only way to
+# learn about the function is to evaluate it at every point. We're ultimately interested in cases
+# where there are too many x-values to evaluate each function value and recover the period in a
+# brute force manner.
+#
+# What does period finding have to do with groups? Well, in the language of group theory, the
+# integers from 0 to 11 (together with an appropriate operation, like addition modulo 12) form a
+# so-called *cyclic group*, which is an example of *Abelian* groups that Aaronson referred to above. 
+#
+# .. admonition:: Abelian group
+#     :class: note
+#
+#     A group is a set of elements that has:
path: demonstrations/tutorial_period_finding.py
user: login: mariaschuld
body: @alvaro-at-xanadu @CatalinaAlbornoz do you have any idea why this is renders bold?
created_at: 2025-04-07T07:50:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,392 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+.. figure:: ../_static/demo_thumbnails/opengraph_demo_thumbnails/OGthumbnail_period_finding.png
+    :align: center
+    :width: 70%
+    :target: javascript:void(0)
+
path: demonstrations/tutorial_period_finding.py
user: login: cognigami
body: ```suggestion
```
created_at: 2025-04-15T16:45:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
path: demonstrations/tutorial_period_finding.py
user: login: justinpickering
body: Will need to be consistent with the title in the metadata. Using colons is also consistent with other demo titles.

```suggestion
Period finding: A problem at the heart of quantum computing
```
created_at: 2025-04-16T01:42:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,62 @@
+{
+    "title": "Period finding: A problem at the heart of quantum computing",
+    "authors": [
+        {
+            "username": "mariaschuld"
+        }
+    ],
+    "dateOfPublication": "2025-04-16T10:00:00+00:00",
+    "dateOfLastModification": "2025-04-16T10:00:00+00:00",
+    "categories": [
+        "Getting Started",
+        "Quantum Computing"
+    ],
path: demonstrations/tutorial_period_finding.metadata.json
user: login: justinpickering
body: Just want to confirm this is a 'Getting Started' category demo? Typically these are 'how to' demos and less advanced topics.

Here are other Categories we can choose from. Any others that apply (can have multiple).
![image](https://github.com/user-attachments/assets/0f1007ac-4311-4278-9fb6-7838e6138dde)

created_at: 2025-04-16T01:45:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,387 @@
+r"""
+Period finding -- A problem at the heart of quantum computing
+=============================================================
+
+You might have heard that Shor's algorithm is an instance of "period finding". You might also have heard that, more generally, this is an example of an *Abelian hidden subgroup problem* solved by a quantum computer's stunning ability to implement the Fourier transform efficiently for an intractable number of function values. Hidden subgroup problems and quantum Fourier transforms were all the rage in the quantum computing literature in the 2000s.
+
+While trends may have moved on, the idea of extracting group structure from the Fourier spectrum is still at the very core of what quantum computers could be useful for. Scott Aaronson, for example, in his 2022 commentary "How Much Structure Is Needed for Huge Quantum Speedup?" presents the following hierarchy:
+
+.. figure:: ../_static/demonstration_assets/period_finding/aaronson_fig6.png
+    :align: center
+    :width: 75%
+    :target: javascript:void(0);
+
+    Figure 1. Aaronson's 'hierarchy of structure' for quantum algorithms (Fig. 6 in [#Aaronson22]_)
+
+However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of a integer valued function—something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
path: demonstrations/tutorial_period_finding.py
user: login: justinpickering
body: ```suggestion
However, group theory is a huge hurdle for even some of the more seasoned quantum enthusiasts. This demo wants to give a glimpse of what this "Abelian structure" is all about. Luckily, the fruit-fly example of a hidden subgroup problem is just the task of finding the period of an integer valued function—something one can appreciate without much group jargon. A fantastic resource to read up on the basics is the review of hidden subgroup problems by Childs & van Dam (2010) [#Childs2010]_.
```
created_at: 2025-04-16T01:51:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,62 @@
+{
+    "title": "Period finding: A problem at the heart of quantum computing",
+    "authors": [
+        {
+            "username": "mariaschuld"
+        }
+    ],
+    "dateOfPublication": "2025-04-16T10:00:00+00:00",
+    "dateOfLastModification": "2025-04-16T10:00:00+00:00",
+    "categories": [
+        "Getting Started",
+        "Quantum Computing"
+    ],
+    "tags": [],
+    "previewImages": [
+        {
+            "type": "thumbnail",
+            "uri": "/_static/demo_thumbnails/regular_demo_thumbnails/thumbnail_period_finding.png"
+        },
+        {
+            "type": "large_thumbnail",
+            "uri": "/_static/demo_thumbnails/large_demo_thumbnails/thumbnail_large_period_finding.png"
+        }
+    ],
+    "seoDescription": "Find out how quantum computers exploit group structure.",
path: demonstrations/tutorial_period_finding.metadata.json
user: login: justinpickering
body: The seoDescription is quite short. Could it be lengthened and improved to make mention of more of the key words?

Perhaps:

```suggestion
    "seoDescription": "Find out how quantum computers leverage group structure and the Fourier transform to uncover hidden patterns in functions.",
```
created_at: 2025-04-16T01:57:46Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-10T18:57:48Z
body: 👋 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!
author_association: NONE
user: login: mariaschuld
created_at: 2025-03-11T07:33:23Z
body: @alvaro-at-xanadu, it would be great if you'd have a quick look at why the build is failing...your experience might save us a lot of time guessing what's wrong with the metadata :) 

PS: I failed to run the json file check locally, because I didn't know what to put for `<largest_number>` in `/demo.metadata.schema.<largest_number>.json`...maybe I missed it somewhere

author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-13T14:23:09Z
body: **Thank you for opening this pull request.**

You can find the built site [at this link](https://qml-build-previews.pennylane.ai/pull_request_build_preview/1324/qml/demonstrations/).

**Deployment Info:**
- Pull Request ID: `1324`
- Deployment SHA: `8b9138f35f40f7d7e727fc2873439402a272210c`
(The `Deployment SHA` refers to the latest commit hash the docs were built from)

**Note:** It may take several minutes for updates to this pull request to be reflected on the deployed site.

<!-- default-pinned-comment -->
author_association: NONE
user: login: mariaschuld
created_at: 2025-03-20T07:31:56Z
body: @alvaro-at-xanadu I'd like to add Petar Simidzija as a reviewer... but his github handle @simidzija is not available. Does he need to join the repo somehow? Thanks!

author_association: CONTRIBUTOR
user: login: mariaschuld
created_at: 2025-03-20T08:37:24Z
body: @alvaro-at-xanadu , ready for review. Thanks @CatalinaAlbornoz  :heart: 
author_association: CONTRIBUTOR
user: login: mariaschuld
created_at: 2025-03-24T07:01:14Z
body: Thanks @simidzija, ready for re-review! I implemented all your suggestions, which were good.

If you're happy with the demo, you can submit an approval, or request more changes.

author_association: CONTRIBUTOR
user: login: mariaschuld
created_at: 2025-03-31T15:40:48Z
body: @alvaro-at-xanadu the demo is ready for whatever you want to do with it... just kindly confirm that everything looks ok from your side?
author_association: CONTRIBUTOR
user: login: CatalinaAlbornoz
created_at: 2025-04-01T18:44:07Z
body: Awesome @mariaschuld !!

Normally at this point Alvaro assigns someone to do a Product review.

I took a quick glance and noticed a couple of things:

1. The images are opening on a new tab when I click on them. I can fix this assuming it's not intended.
2. Most figures don't have caption. I think it would be good to add a line below the figure saying "Figure X. caption for the figure". This should be slightly indented so that it shows up below the figure. I can help with the formatting for this. All I'd need is the text of the caption.
3. In that same line, the first figure is called Figure 6. I know this is because the figure comes from a different source, where it was figure 6, but I think it can be confusing. I'd recommend cropping the current caption and adding a new one called Figure 1. and citing [Scott Aaronson's preprint](https://arxiv.org/abs/2209.06930) in the caption.
4. The first plot in "Implementation" has no title or labels for the axes. It can be useful to add them to better understand the figure.

That's all from my side! These are just suggestions. Feel free to discuss them with Alvaro or the reviewer from the Product side if you disagree with any of them.
author_association: CONTRIBUTOR
user: login: mariaschuld
created_at: 2025-04-02T07:03:51Z
body: > Awesome @mariaschuld !!
> 
> Normally at this point Alvaro assigns someone to do a Product review.
> 
> I took a quick glance and noticed a couple of things:
> 
> 1. The images are opening on a new tab when I click on them. I can fix this assuming it's not intended.
> 2. Most figures don't have caption. I think it would be good to add a line below the figure saying "Figure X. caption for the figure". This should be slightly indented so that it shows up below the figure. I can help with the formatting for this. All I'd need is the text of the caption.
> 3. In that same line, the first figure is called Figure 6. I know this is because the figure comes from a different source, where it was figure 6, but I think it can be confusing. I'd recommend cropping the current caption and adding a new one called Figure 1. and citing [Scott Aaronson's preprint](https://arxiv.org/abs/2209.06930) in the caption.
> 4. The first plot in "Implementation" has no title or labels for the axes. It can be useful to add them to better understand the figure.
> 
> That's all from my side! These are just suggestions. Feel free to discuss them with Alvaro or the reviewer from the Product side if you disagree with any of them.

Thanks so much, these are all great! I think I found the trick to not open images, but if you have a suggestion of how to format the captions (mentioned in comments) that would be great.
author_association: CONTRIBUTOR
user: login: CatalinaAlbornoz
created_at: 2025-04-03T23:53:20Z
body: @mariaschuld I've added some captions for the figures. Let me know if they look good to you or if we should make any changes.
author_association: CONTRIBUTOR
user: login: mariaschuld
created_at: 2025-04-07T08:10:23Z
body: > Just minor copy things, and then we're ready to go! @mariaschuld I marked it as approved, but please do take a look at the minor copy-edits and commit if you agree :)

Thanks @alvaro-at-xanadu. I found one more rendering issue marked in the comments, but accepted all of your suggestions. 
author_association: CONTRIBUTOR