Module hybridq.extras.random
Author: Salvatore Mandra (salvatore.mandra@nasa.gov)
Copyright © 2021, United States Government, as represented by the Administrator of the National Aeronautics and Space Administration. All rights reserved.
The HybridQ: A Hybrid Simulator for Quantum Circuits platform is licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software distributed under the License 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.
Expand source code
"""
Author: Salvatore Mandra (salvatore.mandra@nasa.gov)
Copyright © 2021, United States Government, as represented by the Administrator
of the National Aeronautics and Space Administration. All rights reserved.
The HybridQ: A Hybrid Simulator for Quantum Circuits platform is licensed under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software distributed
under the License 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 __future__ import annotations
from hybridq.gate.utils import get_available_gates, get_clifford_gates
from hybridq.gate import Gate, MatrixGate
from hybridq.circuit import Circuit
import numpy as np
def get_indexes(n_qubits: int, *, use_random_indexes: bool = False):
# Initialize
indexes = []
# Randomize indexes
if use_random_indexes:
# Add strings
indexes = []
while len(indexes) < n_qubits // 3:
indexes += [
''.join(
np.random.choice(list('abcdefghijklmnopqrstuvwxyz'),
size=20))
for _ in range(n_qubits // 3 - len(indexes))
]
# Add tuples
while len(indexes) < n_qubits:
indexes += [
tuple(x) for x in np.unique(np.random.randint(
-2**32 + 1, 2**32 - 1, size=(n_qubits - len(indexes), 2)),
axis=0)
]
# Random permutation
indexes = [indexes[i] for i in np.random.permutation(n_qubits)]
# Use sequential
else:
indexes = np.arange(n_qubits)
# Return indexes
return indexes
def get_random_gate(randomize_power: bool = True,
use_clifford_only: bool = False,
use_unitary_only: bool = True):
"""
Generate random gate.
"""
# Get available gates
avail_gates = get_clifford_gates(
) if use_clifford_only else get_available_gates()
# Add random matrices
if not use_unitary_only:
avail_gates = avail_gates + ('RANDOM_MATRIX',)
# Get random gate
gate_name = np.random.choice(avail_gates)
# Generate a random matrix
if gate_name == 'RANDOM_MATRIX':
# Get random number of qubits
n_qubits = np.random.choice(range(1, 3))
# Get random matrix
M = 2 * np.random.random(
(2**n_qubits, 2**n_qubits)).astype('complex') - 1
M += 1j * (2 * np.random.random((2**n_qubits, 2**n_qubits)) - 1)
M /= 2
# Normalize random matrix
M /= np.sqrt(np.linalg.norm(np.linalg.eigvalsh(M.conj().T @ M)))
# Get gate
gate = MatrixGate(M)
# Generate named gate
else:
gate = Gate(gate_name)
# Apply random parameters if present
if gate.provides('params'):
gate._set_params(np.random.random(size=gate.n_params))
# Apply random power
gate = gate**(2 * np.random.random() - 1 if randomize_power else 1)
# Apply conjugation if supported
if gate.provides('conj') and np.random.random() < 0.5:
gate._conj()
# Apply transposition if supported
if gate.provides('T') and np.random.random() < 0.5:
gate._T()
# Convert to MatrixGate half of the times
gate = gate if gate.name == 'MATRIX' or np.random.random(
) < 0.5 else MatrixGate(gate.matrix())
# Return gate
return gate
def get_rqc(n_qubits: int,
n_gates: int,
*,
indexes: list[int] = None,
randomize_power: bool = True,
use_clifford_only: bool = False,
use_unitary_only: bool = True,
use_random_indexes: bool = False,
verbose: bool = False):
"""
Generate random quantum circuit.
"""
from tqdm.auto import tqdm
# Initialize circuit
circuit = Circuit()
# If not provided, generate indexes
indexes = get_indexes(n_qubits, use_random_indexes=use_random_indexes
) if indexes is None else list(indexes)
# Check that size is correct
assert (len(indexes) == n_qubits)
# Get random gates
gates = (get_random_gate(randomize_power=randomize_power,
use_unitary_only=use_unitary_only,
use_clifford_only=use_clifford_only)
for _ in range(n_gates))
# Assign random qubits, and return circuit
return Circuit(
gate.on([
indexes[i]
for i in np.random.choice(n_qubits, gate.n_qubits, replace=False)
])
for gate in tqdm(gates,
disable=not verbose,
total=n_gates,
desc='Generating random circuit'))
Functions
def get_indexes(n_qubits: int, *, use_random_indexes: bool = False)
-
Expand source code
def get_indexes(n_qubits: int, *, use_random_indexes: bool = False): # Initialize indexes = [] # Randomize indexes if use_random_indexes: # Add strings indexes = [] while len(indexes) < n_qubits // 3: indexes += [ ''.join( np.random.choice(list('abcdefghijklmnopqrstuvwxyz'), size=20)) for _ in range(n_qubits // 3 - len(indexes)) ] # Add tuples while len(indexes) < n_qubits: indexes += [ tuple(x) for x in np.unique(np.random.randint( -2**32 + 1, 2**32 - 1, size=(n_qubits - len(indexes), 2)), axis=0) ] # Random permutation indexes = [indexes[i] for i in np.random.permutation(n_qubits)] # Use sequential else: indexes = np.arange(n_qubits) # Return indexes return indexes
def get_random_gate(randomize_power: bool = True, use_clifford_only: bool = False, use_unitary_only: bool = True)
-
Generate random gate.
Expand source code
def get_random_gate(randomize_power: bool = True, use_clifford_only: bool = False, use_unitary_only: bool = True): """ Generate random gate. """ # Get available gates avail_gates = get_clifford_gates( ) if use_clifford_only else get_available_gates() # Add random matrices if not use_unitary_only: avail_gates = avail_gates + ('RANDOM_MATRIX',) # Get random gate gate_name = np.random.choice(avail_gates) # Generate a random matrix if gate_name == 'RANDOM_MATRIX': # Get random number of qubits n_qubits = np.random.choice(range(1, 3)) # Get random matrix M = 2 * np.random.random( (2**n_qubits, 2**n_qubits)).astype('complex') - 1 M += 1j * (2 * np.random.random((2**n_qubits, 2**n_qubits)) - 1) M /= 2 # Normalize random matrix M /= np.sqrt(np.linalg.norm(np.linalg.eigvalsh(M.conj().T @ M))) # Get gate gate = MatrixGate(M) # Generate named gate else: gate = Gate(gate_name) # Apply random parameters if present if gate.provides('params'): gate._set_params(np.random.random(size=gate.n_params)) # Apply random power gate = gate**(2 * np.random.random() - 1 if randomize_power else 1) # Apply conjugation if supported if gate.provides('conj') and np.random.random() < 0.5: gate._conj() # Apply transposition if supported if gate.provides('T') and np.random.random() < 0.5: gate._T() # Convert to MatrixGate half of the times gate = gate if gate.name == 'MATRIX' or np.random.random( ) < 0.5 else MatrixGate(gate.matrix()) # Return gate return gate
def get_rqc(n_qubits: int, n_gates: int, *, indexes: list[int] = None, randomize_power: bool = True, use_clifford_only: bool = False, use_unitary_only: bool = True, use_random_indexes: bool = False, verbose: bool = False)
-
Generate random quantum circuit.
Expand source code
def get_rqc(n_qubits: int, n_gates: int, *, indexes: list[int] = None, randomize_power: bool = True, use_clifford_only: bool = False, use_unitary_only: bool = True, use_random_indexes: bool = False, verbose: bool = False): """ Generate random quantum circuit. """ from tqdm.auto import tqdm # Initialize circuit circuit = Circuit() # If not provided, generate indexes indexes = get_indexes(n_qubits, use_random_indexes=use_random_indexes ) if indexes is None else list(indexes) # Check that size is correct assert (len(indexes) == n_qubits) # Get random gates gates = (get_random_gate(randomize_power=randomize_power, use_unitary_only=use_unitary_only, use_clifford_only=use_clifford_only) for _ in range(n_gates)) # Assign random qubits, and return circuit return Circuit( gate.on([ indexes[i] for i in np.random.choice(n_qubits, gate.n_qubits, replace=False) ]) for gate in tqdm(gates, disable=not verbose, total=n_gates, desc='Generating random circuit'))