Module hybridq.circuit.simulation.utils
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.
Types
Array
: numpy.ndarray
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.
Types
-----
**`Array`**: `numpy.ndarray`
"""
from __future__ import annotations
from hybridq.utils import sort, argsort
import numpy as np
import numba
@numba.vectorize
def _parity(v):
v ^= v >> 16
v ^= v >> 8
v ^= v >> 4
v &= 0xf
if (0x6996 >> v) & 1:
return -1
else:
return 1
def prepare_state(state: str,
d: {int, iter[int]} = 2,
complex_type: any = 'complex64'):
"""
Prepare initial state accordingly to `state`.
Parameters
----------
state: str
State used to prepare the quantum state. If `state` is a string, a
quantum state of `len(str)` is created using the following notation:
- `0`: qubit is set to `0` in the computational basis,
- `1`: qubit is set to `1` in the computational basis,
- `+`: qubit is set to `+` state in the computational basis,
- `-`: qubit is set to `-` state in the computational basis.
d: {int, iter[int]}
Dimensions of qubits.
complex_type: any, optional
Complex type to use to prepare the quantum state.
Returns
-------
Array
Quantum state prepared from `state`.
Example
-------
>>> prepare_state('+-+')
array([ 0.35355338+0.j, 0.35355338+0.j, -0.35355338+0.j, -0.35355338+0.j,
0.35355338+0.j, 0.35355338+0.j, -0.35355338+0.j, -0.35355338+0.j],
dtype=complex64)
"""
# Convert state to str
try:
state = str(state)
except:
raise ValueError("'state' must be convertible to 'str'.")
# Get dimensions
try:
d = (int(d),) * len(state)
except:
d = tuple(int(x) for x in d)
# Check that only allowed symbols are contained
if set(state).difference('+-01'):
raise ValueError(
f"Symbols {set(state).difference('+-01')} are not allowed.")
# Check no dimensions are zero
if (isinstance(d, int) and d <= 0) or (isinstance(d, tuple) and
any(d <= 0 for d in d)):
raise ValueError("All dimensions must be positive")
# Check n_qubits and dimensions are consistent
if len(d) != len(state):
raise ValueError("Number of qubits and dimensions are not consistent.")
# Simple cases
if not set(state).difference(['0', '1']):
# Initialize in the computational basis
q_state = np.zeros(d, dtype=complex_type)
q_state[tuple(int(b) for b in state)] = 1
elif set(state) == {'+'}:
# Initialize to superposition
q_state = np.ones(d, dtype=complex_type) / np.sqrt(np.prod(d))
else:
# Get order
order = argsort(state,
key=lambda x: {
'0': 0,
'1': 1,
'+': 2,
'-': 3
}[x])
order = [order.index(i) for i in range(len(order))]
# Get dimensions
d_a0 = tuple(d for d, b in zip(d, state) if b in '0')
d_a1 = tuple(d for d, b in zip(d, state) if b in '1')
d_b = tuple(d for d, b in zip(d, state) if b == '+')
d_c = tuple(d for d, b in zip(d, state) if b == '-')
# Get size
p_a0 = np.prod(d_a0).astype(int)
p_a1 = np.prod(d_a1).astype(int)
p_b = np.prod(d_b).astype(int)
p_c = np.prod(d_c).astype(int)
# Get number of dimensions
n_a0 = len(d_a0)
n_a1 = len(d_a1)
n_b = len(d_b)
n_c = len(d_c)
# Check
assert (n_a0 + n_a1 + n_b + n_c == len(state))
# Initialize state
q_state = np.zeros((p_a0 * p_a1, p_b, p_c), dtype=complex_type)
# Assign state
q_state[p_a1 - 1] = np.reshape(
np.kron(np.ones(p_b), _parity(np.arange(p_c))) / np.sqrt(p_b * p_c),
(p_b, p_c))
# Reshape and transpose
q_state = np.reshape(
np.reshape(
np.transpose(np.reshape(q_state, d_a0 + d_a1 + d_b + d_c),
order), (p_a0 * p_a1 * p_b * p_c,)), d)
# Return state
return q_state
Functions
def prepare_state(state: str, d: {int, iter[int]} = 2, complex_type: any = 'complex64')
-
Prepare initial state accordingly to
state
.Parameters
state
:str
-
State used to prepare the quantum state. If
state
is a string, a quantum state oflen(str)
is created using the following notation:0
: qubit is set to0
in the computational basis,1
: qubit is set to1
in the computational basis,+
: qubit is set to+
state in the computational basis,-
: qubit is set to-
state in the computational basis.
d
:{int, iter[int]}
- Dimensions of qubits.
complex_type
:any
, optional- Complex type to use to prepare the quantum state.
Returns
Array
- Quantum state prepared from
state
.
Example
>>> prepare_state('+-+') array([ 0.35355338+0.j, 0.35355338+0.j, -0.35355338+0.j, -0.35355338+0.j, 0.35355338+0.j, 0.35355338+0.j, -0.35355338+0.j, -0.35355338+0.j], dtype=complex64)
Expand source code
def prepare_state(state: str, d: {int, iter[int]} = 2, complex_type: any = 'complex64'): """ Prepare initial state accordingly to `state`. Parameters ---------- state: str State used to prepare the quantum state. If `state` is a string, a quantum state of `len(str)` is created using the following notation: - `0`: qubit is set to `0` in the computational basis, - `1`: qubit is set to `1` in the computational basis, - `+`: qubit is set to `+` state in the computational basis, - `-`: qubit is set to `-` state in the computational basis. d: {int, iter[int]} Dimensions of qubits. complex_type: any, optional Complex type to use to prepare the quantum state. Returns ------- Array Quantum state prepared from `state`. Example ------- >>> prepare_state('+-+') array([ 0.35355338+0.j, 0.35355338+0.j, -0.35355338+0.j, -0.35355338+0.j, 0.35355338+0.j, 0.35355338+0.j, -0.35355338+0.j, -0.35355338+0.j], dtype=complex64) """ # Convert state to str try: state = str(state) except: raise ValueError("'state' must be convertible to 'str'.") # Get dimensions try: d = (int(d),) * len(state) except: d = tuple(int(x) for x in d) # Check that only allowed symbols are contained if set(state).difference('+-01'): raise ValueError( f"Symbols {set(state).difference('+-01')} are not allowed.") # Check no dimensions are zero if (isinstance(d, int) and d <= 0) or (isinstance(d, tuple) and any(d <= 0 for d in d)): raise ValueError("All dimensions must be positive") # Check n_qubits and dimensions are consistent if len(d) != len(state): raise ValueError("Number of qubits and dimensions are not consistent.") # Simple cases if not set(state).difference(['0', '1']): # Initialize in the computational basis q_state = np.zeros(d, dtype=complex_type) q_state[tuple(int(b) for b in state)] = 1 elif set(state) == {'+'}: # Initialize to superposition q_state = np.ones(d, dtype=complex_type) / np.sqrt(np.prod(d)) else: # Get order order = argsort(state, key=lambda x: { '0': 0, '1': 1, '+': 2, '-': 3 }[x]) order = [order.index(i) for i in range(len(order))] # Get dimensions d_a0 = tuple(d for d, b in zip(d, state) if b in '0') d_a1 = tuple(d for d, b in zip(d, state) if b in '1') d_b = tuple(d for d, b in zip(d, state) if b == '+') d_c = tuple(d for d, b in zip(d, state) if b == '-') # Get size p_a0 = np.prod(d_a0).astype(int) p_a1 = np.prod(d_a1).astype(int) p_b = np.prod(d_b).astype(int) p_c = np.prod(d_c).astype(int) # Get number of dimensions n_a0 = len(d_a0) n_a1 = len(d_a1) n_b = len(d_b) n_c = len(d_c) # Check assert (n_a0 + n_a1 + n_b + n_c == len(state)) # Initialize state q_state = np.zeros((p_a0 * p_a1, p_b, p_c), dtype=complex_type) # Assign state q_state[p_a1 - 1] = np.reshape( np.kron(np.ones(p_b), _parity(np.arange(p_c))) / np.sqrt(p_b * p_c), (p_b, p_c)) # Reshape and transpose q_state = np.reshape( np.reshape( np.transpose(np.reshape(q_state, d_a0 + d_a1 + d_b + d_c), order), (p_a0 * p_a1 * p_b * p_c,)), d) # Return state return q_state