Source code for mitiq.benchmarks.quantum_volume_circuits

# Copyright (C) Unitary Fund
#
# This source code is licensed under the GPL license (v3) found in the
# LICENSE file in the root directory of this source tree.

"""Functions for creating circuits of the form used in quantum
volume experiments as defined in https://arxiv.org/abs/1811.12926.

Useful overview of quantum volume experiments:
https://pennylane.ai/qml/demos/quantum_volume

Cirq implementation of quantum volume circuits:
cirq-core/cirq/contrib/quantum_volume/quantum_volume.py
"""

from typing import Optional, Sequence, Tuple

from cirq import decompose as cirq_decompose
from cirq.circuits import Circuit
from cirq.contrib.quantum_volume import (
    compute_heavy_set,
    generate_model_circuit,
)
from cirq.value import big_endian_int_to_bits
from numpy import random

from mitiq import QPROGRAM, Bitstring
from mitiq.interface import convert_from_mitiq


[docs] def generate_quantum_volume_circuit( num_qubits: int, depth: int, decompose: bool = False, seed: Optional[int] = None, return_type: Optional[str] = None, ) -> Tuple[QPROGRAM, Sequence[Bitstring]]: """Generate a quantum volume circuit with the given number of qubits and depth. The generated circuit consists of `depth` layers of random qubit permutations followed by random two-qubit gates that are sampled from the Haar measure on SU(4). Args: num_qubits: The number of qubits in the generated circuit. depth: The number of layers in the generated circuit. decompose: Recursively decomposes the randomly sampled (numerical) unitary matrix gates into simpler gates. seed: Seed for generating random circuit. return_type: String which specifies the type of the returned circuits. See the keys of ``mitiq.SUPPORTED_PROGRAM_TYPES`` for options. If ``None``, the returned circuits have type ``cirq.Circuit``. Returns: A quantum volume circuit acting on ``num_qubits`` qubits. A list of the heavy bitstrings for the returned circuit. """ random_state = random.RandomState(seed) circuit = generate_model_circuit( num_qubits, depth, random_state=random_state ) heavy_bitstrings = compute_heavy_bitstrings(circuit, num_qubits) if decompose: # Decompose random unitary gates into simpler gates. circuit = Circuit(cirq_decompose(circuit)) return_type = "cirq" if not return_type else return_type return convert_from_mitiq(circuit, return_type), heavy_bitstrings
[docs] def compute_heavy_bitstrings( circuit: Circuit, num_qubits: int, ) -> Sequence[Bitstring]: """Classically compute the heavy bitstrings of the provided circuit. The heavy bitstrings are defined as the output bit-strings that have a greater than median probability of being generated. Args: circuit: The circuit to classically simulate. Returns: A list containing the heavy bitstrings. """ heavy_vals = compute_heavy_set(circuit) # Convert base-10 ints to Bitstrings. heavy_bitstrings = [ big_endian_int_to_bits(val, bit_count=num_qubits) for val in heavy_vals ] return heavy_bitstrings