In computational complexity theory and quantum computing, Simon's problem is a computational problem that is proven to be solved exponentially faster on a quantum computer than on a classical (that is, traditional) computer. The quantum algorithm solving Simon's problem, usually called Simon's algorithm, served as the inspiration for Shor's algorithm.[1] Both problems are special cases of the abelian hidden subgroup problem, which is now known to have efficient quantum algorithms.

The problem is set in the model of decision tree complexity or query complexity and was conceived by Daniel R. Simon in 1994.[2] Simon exhibited a quantum algorithm that solves Simon's problem exponentially faster with exponentially fewer queries than the best probabilistic (or deterministic) classical algorithm. In particular, Simon's algorithm uses a linear number of queries and any classical probabilistic algorithm must use an exponential number of queries.

This problem yields an oracle separation between the complexity classes BPP (bounded-error classical query complexity) and BQP (bounded-error quantum query complexity).[3] This is the same separation that the Bernstein–Vazirani algorithm achieves, and different from the separation provided by the Deutsch–Jozsa algorithm, which separates P and EQP. Unlike the Bernstein–Vazirani algorithm, Simon's algorithm's separation is exponential.

Because this problem assumes the existence of a highly-structured "black box" oracle to achieve its speedup, this problem has little practical value.[4] However, without such an oracle, exponential speedups cannot easily be proven, since this would prove that P is different from PSPACE.

Problem description

Simon's problem considers access to a function as implemented by a black box or an oracle. This function is promised to be either a one-to-one function, or a two-to-one function; if is two-to-one, it is furthermore promised that two inputs and evaluate to the same value if and only if and differ in a fixed set of bits. I.e.,

If is not one-to-one, it is promised that there exists a non-zero such that, for all , if and only if

where denotes bitwise exclusive-or. Simon's problem asks, in its decision version, whether is one-to-one or two-to-one. In its non-decision version, Simon's problem asks whether is one-to-one or what is the value of (as defined above). The goal is to solve this task with the least number of queries (evaluations) of .

Note that if , then and with . On the other hand (because for all and ), . Thus, Simon's problem may be restated in the following form:

Given black-box or oracle access to , promised to satisfy, for some and all , if and only if , determine whether (decision version), or output (non-decision version).

Note also that the promise on implies that if is two-to-one then it is a periodic function:

Example

The following function is an example of a function that satisfies the required property for :

000 101
001 010
010 000
011 110
100 000
101 110
110 101
111 010

In this case, (i.e. the solution). Every output of occurs twice, and the two input strings corresponding to any one given output have bitwise XOR equal to .

For example, the input strings and are both mapped (by ) to the same output string . That is, and . Applying XOR to 010 and 100 obtains 110, that is

can also be verified using input strings 001 and 111 that are both mapped (by f) to the same output string 010. Applying XOR to 001 and 111 obtains 110, that is . This gives the same solution as before.

In this example the function f is indeed a two-to-one function where .

Problem hardness

Intuitively, this is a hard problem to solve in a "classical" way, even if one uses randomness and accepts a small probability of error. The intuition behind the hardness is reasonably simple: if you want to solve the problem classically, you need to find two different inputs and for which . There is not necessarily any structure in the function that would help us to find two such inputs: more specifically, we can discover something about (or what it does) only when, for two different inputs, we obtain the same output. In any case, we would need to guess different inputs before being likely to find a pair on which takes the same output, as per the birthday problem. Since, classically to find s with a 100% certainty it would require checking inputs, Simon's problem seeks to find s using fewer queries than this classical method.

Simon's algorithm

Quantum circuit representing/implementing Simon's algorithm

The algorithm as a whole uses a subroutine to execute the following two steps:

  1. Run the quantum subroutine an expected times to get a list of linearly independent bitstrings .
  2. Each satisfies , so we can solve the system of equations this produces to get .

Quantum subroutine

The quantum circuit (see the picture) is the implementation of the quantum part of Simon's algorithm. The quantum subroutine of the algorithm makes use of the Hadamard transformwhere , where denotes XOR.

First, the algorithm starts with two registers, initialized to . Then, we apply the Hadamard transform to the first register, which gives the state

Query the oracle to get the state

.

Apply another Hadamard transform to the first register. This will produce the state

Finally, we measure the first register (the algorithm also works if the second register is measured before the first, but this is unnecessary). The probability of measuring a state isThis is due to the fact that taking the magnitude of this vector and squaring it sums up all the probabilities of all the possible measurements of the second register that must have the first register as . There are two cases for our measurement:

  1. and is one-to-one.
  2. and is two-to-one.

For the first case, since in this case, is one-to-one, implying that the range of is , meaning that the summation is over every basis vector. For the second case, note that there exist two strings, and , such that , where . Thus, Furthermore, since , , and soThis expression is now easy to evaluate. Recall that we are measuring . When , then this expression will evaluate to , and when , then this expression will be .

Thus, both when and when , our measured satisfies .

Classical post-processing

We run the quantum part of the algorithm until we have a linearly independent list of bitstrings , and each satisfies . Thus, we can efficiently solve this system of equations classically to find .

The probability that are linearly independent is at leastOnce we solve the system of equations, and produce a solution , we can test if . If this is true, then we know , since . If it is the case that , then that means that , and since is one-to-one.

We can repeat Simon's algorithm a constant number of times to increase the probability of success arbitrarily, while still having the same time complexity.

Explicit examples of Simon's algorithm for few qubits

One qubit

Consider the simplest instance of the algorithm, with . In this case evolving the input state through an Hadamard gate and the oracle results in the state (up to renormalization):

If , that is, , then measuring the second register always gives the outcome , and always results in the first register collapsing to the state (up to renormalization):

Thus applying an Hadamard and measuring the first register always gives the outcome . On the other hand, if is one-to-one, that is, , then measuring the first register after the second Hadamard can result in both and , with equal probability.

We recover from the measurement outcomes by looking at whether we measured always , in which case , or we measured both and with equal probability, in which case we infer that . This scheme will fail if but we nonetheless always found the outcome , but the probability of this event is with the number of performed measurements, and can thus be made exponentially small by increasing the statistics.

Two qubits

Consider now the case with . The initial part of the algorithm results in the state (up to renormalization):If , meaning is injective, then finding on the second register always collapses the first register to , for all . In other words, applying Hadamard gates and measuring the first register the four outcomes are thus found with equal probability.

Suppose on the other hand , for example, . Then measuring on the second register collapses the first register to the state . And more generally, measuring gives on the first register. Applying Hadamard gates and measuring on the first register can thus result in the outcomes and with equal probabilities.

Similar reasoning applies to the other cases: if then the possible outcomes are and , while if the possible outcomes are and , compatibly with the rule discussed in the general case.

To recover we thus only need to distinguish between these four cases, collecting enough statistics to ensure that the probability of mistaking one outcome probability distribution for another is sufficiently small.

Complexity

Simon's algorithm requires queries to the black box, whereas a classical algorithm would need at least queries. It is also known that Simon's algorithm is optimal in the sense that any quantum algorithm to solve this problem requires queries.[5][6]

Simon's algorithm Qiskit implementation

Below is an example of how one might implement Simon’s algorithm in Python using Qiskit (version 1.3), an open-source quantum computing software development framework by IBM. The code includes a simple way to construct the required oracle (the black box) given a “secret” bitstring.

import numpy as np
from qiskit import QuantumCircuit, transpile
from qiskit_aer import Aer
from qiskit.visualization import plot_histogram
from qiskit.circuit.library import MCXGate

def build_simon_oracle(secret_string: str, num_qubits: int) -> QuantumCircuit:
    """
    Build a Qiskit circuit that represents the oracle for Simon's problem.

    The oracle encodes a function f such that f(x) = f(x ⊕ s).
    For demonstration we create random labels for half the inputs,
    ensuring that the same label is used for x and x⊕s.
    """
    # The oracle acts on a 2*n qubit register:
    #   qubits 0 .. n-1: input register
    #   qubits n .. 2*n-1: output register
    oracle = QuantumCircuit(num_qubits * 2)
    s_int = int(secret_string, 2)
    size = 2 ** num_qubits

    # Create random labels for half of the input space.
    rng = np.random.default_rng(123)  # fixed seed for reproducibility
    f_dict = {}
    used_labels = set()
    for x in range(size):
        x_partner = x ^ s_int
        # Assign a label to x and x⊕s if neither has one yet.
        if x not in f_dict and x_partner not in f_dict:
            label = int(rng.integers(0, size))
            while label in used_labels:
                label = int(rng.integers(0, size))
            used_labels.add(label)
            f_dict[x] = label
            f_dict[x_partner] = label

    # For each x, implement the mapping |x>|0...0> -> |x>|f(x)>.
    # For each output bit j for which f(x)_j == '1', add a gate that flips
    # output qubit (n+j) only when the input register equals x.
    for x in range(size):
        label = f_dict[x]
        # Create bit strings for x and f(x) in LSB–first order.
        # This way, bit position i corresponds to input qubit i.
        x_bin = format(x, f'0{num_qubits}b')[::-1]
        label_bin = format(label, f'0{num_qubits}b')[::-1]

        for j, bit in enumerate(label_bin):
            if bit == '1':
                # Create an MCX gate that fires only when the input register equals x.
                mcx_gate = MCXGate(num_qubits, ctrl_state=x_bin)
                # Append the MCX gate:
                #   Controls: input qubits [0, 1, ..., num_qubits-1]
                #   Target: output qubit (num_qubits + j)
                oracle.append(mcx_gate, list(range(num_qubits)) + [num_qubits + j])
    return oracle

def simon_circuit(num_qubits: int, secret_string: str) -> QuantumCircuit:
    """Construct the Simon circuit using the specified oracle."""
    oracle = build_simon_oracle(secret_string, num_qubits)
    qc = QuantumCircuit(num_qubits * 2, num_qubits)

    # 1. Apply Hadamard gates to the input register (qubits 0..n-1).
    for qubit in range(num_qubits):
        qc.h(qubit)

    # 2. Apply the oracle.
    qc.compose(oracle, inplace=True)

    # 3. Apply Hadamard gates to the input register again.
    for qubit in range(num_qubits):
        qc.h(qubit)

    # 4. Measure the input register (qubits 0..n-1).
    for qubit in range(num_qubits):
        qc.measure(qubit, qubit)

    return qc

def run_simon_experiment(num_qubits: int, secret_string: str, shots: int = 1024):
    """Run the Simon circuit on a Qiskit simulator and return measurement counts."""
    circuit = simon_circuit(num_qubits, secret_string)

    # For visual reference (if in a Jupyter notebook, otherwise print text).
    try:
        display(circuit.draw("mpl"))
    except Exception:
        print(circuit.draw())

    simulator = Aer.get_backend('aer_simulator')
    transpiled_circ = transpile(circuit, simulator)
    job = simulator.run(transpiled_circ, shots=shots)
    result = job.result()
    counts = result.get_counts()

    # Display a histogram of the results
    display(plot_histogram(counts))
    return counts

def solve_for_secret(counts, num_qubits):
    """
    Given measurement outcomes from the Simon circuit, solve for s using
    linear algebra in GF(2).

    Note:
      - Qiskit's get_counts() returns bitstrings with the highest-indexed bit on the left.
      - We reverse each bitstring so that index 0 corresponds to qubit 0.
      - The computed secret s is now output in the same order as the secret provided.
    """
    measurement_vectors = []
    for outcome in counts.keys():
        # Reverse the outcome string so that index 0 is qubit 0.
        vec = [int(bit) for bit in reversed(outcome)]
        measurement_vectors.append(vec)

    # Perform Gaussian elimination in GF(2).
    matrix = measurement_vectors[:]  # make a copy
    rank = 0
    for col in range(num_qubits):
        # Find a pivot row with a 1 in the current column.
        pivot = -1
        for row in range(rank, len(matrix)):
            if matrix[row][col] == 1:
                pivot = row
                break
        if pivot < 0:
            continue  # no pivot in this column
        # Swap pivot row into position.
        matrix[rank], matrix[pivot] = matrix[pivot], matrix[rank]
        # Eliminate below the pivot.
        for r2 in range(rank + 1, len(matrix)):
            if matrix[r2][col] == 1:
                matrix[r2] = [a ^ b for a, b in zip(matrix[r2], matrix[rank])]
        rank += 1
        if rank == num_qubits:
            break

    # In Simon's algorithm, the equations j · s = 0 typically span an (n-1)-dimensional space.
    s = [0] * num_qubits
    if rank < num_qubits:
        # Identify pivot columns.
        pivot_positions = []
        for r in range(len(matrix)):
            pivot_col = None
            for c in range(num_qubits):
                if matrix[r][c] == 1:
                    pivot_col = c
                    break
            if pivot_col is not None:
                pivot_positions.append(pivot_col)
        free_cols = [c for c in range(num_qubits) if c not in pivot_positions]
        if free_cols:
            free_col = free_cols[0]
            s[free_col] = 1  # set one free variable to 1
            # Back-substitute for pivot columns.
            for r in reversed(range(len(matrix))):
                pivot_col = None
                for c in range(num_qubits):
                    if matrix[r][c] == 1:
                        pivot_col = c
                        break
                if pivot_col is not None:
                    xor_sum = 0
                    for c in range(pivot_col + 1, num_qubits):
                        xor_sum ^= (matrix[r][c] & s[c])
                    s[pivot_col] = xor_sum

    # Do not reverse the computed secret; it is now in the same order as provided.
    s_str = ''.join(str(bit) for bit in s)
    return s_str

def main():
    # Example: 3 qubits, secret s = "110" (MSB-first notation)
    num_qubits = 3
    secret_string = "110"

    print(f"Running Simon’s Algorithm with secret s = {secret_string}")
    counts = run_simon_experiment(num_qubits, secret_string, shots=2048)

    print("Measurement results (input-register bitstrings):")
    for outcome, cnt in sorted(counts.items()):
        print(f"  {outcome} : {cnt}")

    s_guess = solve_for_secret(counts, num_qubits)
    print(f"\nGuessed secret string from measurements: {s_guess}")

if __name__ == "__main__":
    main()

Output:

Running Simon’s Algorithm with secret s = 110

Example Qiskit quantum circuit that implements Simon's algorithm

Example histogram showing quantum measurement results in Simons algorithm

Measurement results (input-register bitstrings):
  000 : 523
  011 : 512
  100 : 529
  111 : 484

Guessed secret string from measurements: 110

Explanation of Simon's Algorithm Implementation

This program implements Simon’s algorithm to determine a secret bitstring hidden by a function with the property that . Here’s a concise breakdown:

Oracle Construction (build_simon_oracle)

  • Creates a -qubit circuit where the first qubits are the input register and the last are the output register.
  • Assigns random labels to each pair , ensuring that is -to-.
  • Uses multi-controlled X gates (MCXGate) with custom control states to flip output qubits when the input register matches a particular .

Circuit Assembly (simon_circuit)

  • Applies Hadamard gates to the input register to create an equal superposition over all inputs.
  • Inserts the oracle to entangle the input and output registers.
  • Applies another round of Hadamard gates to the input register, which causes interference that ensures every measurement satisfies .
  • Measures the input register to extract these linear constraints on .

Simulation and Measurement (run_simon_experiment)

  • Transpiles and runs the circuit on a Qiskit simulator, collecting measurement counts (bitstrings).

Classical Post-Processing (solve_for_secret)

  • Interprets each measurement outcome as a linear equation in .
  • Uses Gaussian elimination to solve the system, thereby deducing the secret string .

Execution (main)

  • Demonstrates the algorithm with an example (e.g., 3 qubits and ), prints measurement outcomes, and shows the recovered secret.

In summary, the quantum portion of the program prepares a superposition and uses the oracle to encode a hidden periodicity, while the classical portion solves a system of linear equations to deduce the secret .

See also

References

  1. ^ Shor, Peter W. (1999-01-01). "Polynomial-Time Algorithms for Prime Factorization and Discrete Logarithms on a Quantum Computer". SIAM Review. 41 (2): 303–332. arXiv:quant-ph/9508027. doi:10.1137/S0036144598347011. ISSN 0036-1445.
  2. ^ Simon, Daniel R. (1997-10-01). "On the Power of Quantum Computation". SIAM Journal on Computing. 26 (5): 1474–1483. doi:10.1137/S0097539796298637. ISSN 0097-5397.
  3. ^ Preskill, John (1998). Lecture Notes for Physics 229: Quantum Information and Computation. pp. 273–275.
  4. ^ Aaronson, Scott (2018). Introduction to Quantum Information Science Lecture Notes (PDF). pp. 144–151.
  5. ^ Koiran, P.; Nesme, V.; Portier, N. (2007), "The quantum query complexity of the Abelian hidden subgroup problem", Theoretical Computer Science, 380 (1–2): 115–126, doi:10.1016/j.tcs.2007.02.057, retrieved 2011-06-06
  6. ^ Koiran, P.; Nesme, V.; Portier, N. (2005), "A quantum lower bound for the query complexity of Simon's Problem", Proc. ICALP, 3580: 1287–1298, arXiv:quant-ph/0501060, Bibcode:2005quant.ph..1060K, retrieved 2011-06-06
No tags for this post.