diff --git a/README.md b/README.md index 2c9cd560..ce79c854 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,11 @@ Running notebooks locally requires additional dependencies located in [notebooks | Quantum PCA | [Quantum_Principal_Component_Analysis.ipynb](notebooks/advanced_algorithms/Quantum_Principal_Component_Analysis.ipynb) | [He2022](https://ieeexplore.ieee.org/document/9669030) | | QMC | [Quantum_Computing_Quantum_Monte_Carlo.ipynb](notebooks/advanced_algorithms/Quantum_Computing_Quantum_Monte_Carlo.ipynb) | [Motta2018](https://wires.onlinelibrary.wiley.com/doi/10.1002/wcms.1364), [Peruzzo2014](https://www.nature.com/articles/ncomms5213) | + +| Auxiliary functions | Notebook | +| ----- | ----- | +| Random circuit generator | [Random_Circuit.ipynb](notebooks/auxiliary_functions/Random_Circuit.ipynb) | + --- ### Community repos diff --git a/notebooks/auxiliary_functions/Random_Circuit.ipynb b/notebooks/auxiliary_functions/Random_Circuit.ipynb new file mode 100644 index 00000000..40705164 --- /dev/null +++ b/notebooks/auxiliary_functions/Random_Circuit.ipynb @@ -0,0 +1,109 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Random Circuit\n", + "\n", + "Generates random quantum circuits using the Amazon Braket SDK.\n", + "\n", + "### Circuit Generation for Testing\n", + "\n", + "Random quantum circuits allow creation of a diverse set of circuits with a variety of output probability distributions. Users can utilize random circuits to test performance of quantum simulators and QPUs. \n", + "\n", + "### Benchmarking quantum compilation stacks\n", + "\n", + "Random circuits sampled from a fixed gate set (as in the example below) are often used for benchmarking performance of quantum compilation passes, such as circuit mapping, routing, or circuit optimization passes. " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Run on a local simulator" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--Circuit--\n", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │\n", + " ┌───┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ \n", + "q0 : ───●────────────────┤ X ├──────────────┤ XY(0.19) ├───────┤ Rz(4.77) ├───────┤ Rz(0.58) ├─┤ XY(4.59) ├─┤ XY(0.50) ├───●────┤ Rz(5.37) ├───┤ Rz(1.44) ├───────┤ Rz(1.33) ├────────────────────────────────────\n", + " │ └─┬─┘ └────┬─────┘ └──────────┘ └──────────┘ └────┬─────┘ └────┬─────┘ │ └──────────┘ └──────────┘ └──────────┘ \n", + " │ ┌──────────┐ │ ┌──────────┐ │ ┌───┐ ┌──────────┐ ┌──────────┐ │ │ ┌─┴─┐ ┌─────────────┐ ┌───┐ ┌──────────┐ ┌──────────┐ ┌─────────────┐ \n", + "q1 : ───┼───┤ Rx(0.88) ├───┼───┤ Rx(3.18) ├──────┼───────┤ X ├─┤ XY(2.82) ├───────┤ Rx(6.01) ├──────┼────────────┼───────┤ X ├─┤ PHASE(2.33) ├──────────────┤ X ├─┤ XY(2.90) ├───────┤ XY(4.12) ├─┤ PHASE(1.66) ├─\n", + " │ └──────────┘ │ └──────────┘ │ └─┬─┘ └────┬─────┘ └──────────┘ │ │ └───┘ └──────┬──────┘ └─┬─┘ └────┬─────┘ └────┬─────┘ └──────┬──────┘ \n", + " ┌─┴─┐ │ │ │ │ ┌────┴─────┐ │ ┌───┐ │ ┌──────────┐ │ │ ┌────┴─────┐ │ \n", + "q2 : ─┤ X ├────────────────┼─────────────────────┼─────────┼────────┼─────────●────────────────┤ XY(4.59) ├──────┼───────┤ X ├────────●────────┤ Rx(4.41) ├───┼────────┼─────────●───┤ XY(4.12) ├────────┼────────\n", + " └───┘ │ │ │ │ │ └──────────┘ │ └─┬─┘ └──────────┘ │ │ │ └──────────┘ │ \n", + " │ ┌────┴─────┐ │ ┌────┴─────┐ │ ┌──────────┐ ┌────┴─────┐ │ │ │ ┌─┴─┐ │ \n", + "q3 : ──────────────────────┼────────────────┤ XY(0.19) ├───┼───┤ XY(2.82) ├───┼───┤ Rx(2.14) ├──────────────┤ XY(0.50) ├───●──────────────────────────────────┼────────┼───────┤ X ├─────────────────────●────────\n", + " │ └──────────┘ │ └──────────┘ │ └──────────┘ └──────────┘ │ │ └───┘ \n", + " │ │ ┌─┴─┐ ┌──────────┐ ┌──────────┐ │ ┌────┴─────┐ ┌──────────┐ ┌──────────┐ \n", + "q4 : ──────────────────────●───────────────────────────────●────────────────┤ X ├─┤ Rz(5.56) ├─┤ Rz(1.21) ├───────────────────────────────────────────────────●───┤ XY(2.90) ├───────┤ Rz(1.38) ├──┤ Rx(3.39) ├───\n", + " └───┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ \n", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │\n", + "\n", + "--Counts--\n", + "Counter({'01011': 34, '00011': 21, '00001': 13, '00111': 10, '01001': 10, '01010': 3, '00000': 2, '00101': 2, '01000': 2, '10000': 1, '00010': 1, '11010': 1})\n" + ] + } + ], + "source": [ + "from braket.circuits.gates import CNot, Rx, Rz, CPhaseShift, XY\n", + "from braket.devices import LocalSimulator\n", + "from braket.experimental.auxiliary_functions import random_circuit\n", + "\n", + "# Code here\n", + "local_simulator = LocalSimulator()\n", + "gate_set = [CNot, Rx, Rz, CPhaseShift, XY]\n", + "circuit = random_circuit(num_qubits=5, \n", + " num_gates=30,\n", + " gate_set=gate_set,\n", + " seed=42)\n", + "task = local_simulator.run(circuit, shots=100)\n", + "result = task.result()\n", + "print(\"--Circuit--\")\n", + "print(circuit)\n", + "print(\"\\n--Counts--\")\n", + "print(result.measurement_counts)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + }, + "vscode": { + "interpreter": { + "hash": "5904cb9a2089448a2e1aeb5d493d227c9de33e591d7c07e4016fb81e71061a5d" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/src/braket/experimental/auxiliary_functions/__init__.py b/src/braket/experimental/auxiliary_functions/__init__.py new file mode 100644 index 00000000..31f716cf --- /dev/null +++ b/src/braket/experimental/auxiliary_functions/__init__.py @@ -0,0 +1,14 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +from .random_circuit import random_circuit # noqa: F401 diff --git a/src/braket/experimental/auxiliary_functions/random_circuit/__init__.py b/src/braket/experimental/auxiliary_functions/random_circuit/__init__.py new file mode 100644 index 00000000..31f716cf --- /dev/null +++ b/src/braket/experimental/auxiliary_functions/random_circuit/__init__.py @@ -0,0 +1,14 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +from .random_circuit import random_circuit # noqa: F401 diff --git a/src/braket/experimental/auxiliary_functions/random_circuit/random_circuit.md b/src/braket/experimental/auxiliary_functions/random_circuit/random_circuit.md new file mode 100644 index 00000000..b1b16637 --- /dev/null +++ b/src/braket/experimental/auxiliary_functions/random_circuit/random_circuit.md @@ -0,0 +1,7 @@ +The `random_circuit` function generates a random quantum circuit using the Amazon Braket SDK. +The function creates a diverse set of circuits that can be used for testing Braket SDK functionality and compiler benchmarking. + diff --git a/src/braket/experimental/auxiliary_functions/random_circuit/random_circuit.py b/src/braket/experimental/auxiliary_functions/random_circuit/random_circuit.py new file mode 100644 index 00000000..0f5d0af6 --- /dev/null +++ b/src/braket/experimental/auxiliary_functions/random_circuit/random_circuit.py @@ -0,0 +1,62 @@ +import inspect +import math +import random +from typing import List, Optional + +from braket.circuits import Circuit, Gate, Instruction +from braket.circuits.gates import CNot, H, S, T + + +def random_circuit( + num_qubits: int, + num_gates: int, + gate_set: Optional[List[Gate]] = None, + seed: Optional[int] = None, +) -> Circuit: + """ + Generates a random quantum circuit. + + Args: + num_qubits (int): Number of qubits in the circuit. + num_gates (int): Number of instructions (gates) in the circuit. + gate_set (Optional[List[Gate]]): List of basis gates for the random circuit + (default is None). + seed (Optional[int]): Random seed for reproducibility (default is None). + + Returns: + Circuit: random quantum circuit. + """ + # Set the seed if provided + if seed is not None: + random.seed(seed) + + # Default gate_set (Clifford + T) if gate_set is None + if not gate_set: + gate_set = [CNot, S, T, H] + + instructions = [] + for _ in range(num_gates): + gate = random.choice(gate_set) + gate_qubits = gate.fixed_qubit_count() + + # Select random qubits for the gate + qubits = random.sample(range(num_qubits), gate_qubits) + + # Get the constructor's signature to determine required parameters + init_signature = inspect.signature(gate.__init__) + + # Calculate the number of parameters (excluding 'self') + num_params = len(init_signature.parameters) - 1 + + # Generate random parameters for the gate in the range [0, 2*pi] + params = [random.uniform(0, 2 * math.pi) for _ in range(num_params)] + + # Create the gate instance + g = gate(*params) + + # Add the gate as an instruction + instructions.append(Instruction(g, qubits)) + + # Create a circuit with the list of instructions + circuit = Circuit().add(instructions) + return circuit diff --git a/test/unit_tests/braket/experimental/auxiliary_functions/random_circuit/test_random_circuit.py b/test/unit_tests/braket/experimental/auxiliary_functions/random_circuit/test_random_circuit.py new file mode 100644 index 00000000..a38e267b --- /dev/null +++ b/test/unit_tests/braket/experimental/auxiliary_functions/random_circuit/test_random_circuit.py @@ -0,0 +1,39 @@ +from braket.circuits import Circuit +from braket.circuits.gates import XY, CNot, CPhaseShift, H, Rx, Ry, Rz, S, T + +from braket.experimental.auxiliary_functions import random_circuit + + +def test_random_circuit_returns_circuit(): + circuit = random_circuit(num_qubits=3, num_gates=5, seed=1) + assert isinstance(circuit, Circuit) + + +def test_random_circuit_instruction_count(): + num_gates = 5 + circuit = random_circuit(num_qubits=3, num_gates=num_gates, seed=1) + assert len(circuit.instructions) == num_gates + + +def test_random_circuit_consistency_with_seed(): + circuit1 = random_circuit(num_qubits=3, num_gates=5, seed=1) + circuit2 = random_circuit(num_qubits=3, num_gates=5, seed=1) + assert circuit1 == circuit2 + + +def test_random_circuit_variability_without_seed(): + circuit1 = random_circuit(num_qubits=3, num_gates=5) + circuit2 = random_circuit(num_qubits=3, num_gates=5) + assert circuit1 != circuit2 + + +def test_custom_gate_set(): + gate_set = [CNot, H, S, T, Rx, Ry, Rz, XY, CPhaseShift] + circuit = random_circuit(num_qubits=3, num_gates=5, gate_set=gate_set, seed=1) + + gate_from_gate_set = [] + for instr in circuit.instructions: + gate_class = instr.operator.__class__ + gate_from_gate_set.append(gate_class in gate_set) + + assert all(gate_from_gate_set)