pqcprep.phase_tools
Collection of functions relating to phase extraction.
1""" 2Collection of functions relating to phase extraction. 3""" 4import numpy as np 5from qiskit import QuantumCircuit, QuantumRegister 6from .pqc_tools import generate_network, A_generate_network, get_state_vec 7 8def extract_phase(n): 9 r""" 10 11 Constructs an operator that extracts the phase associated with a given computational basis state: 12 $ \ket{k} \mapsto e^{2 \pi k} \ket{k}$. 13 14 This is based on a sequence of single-qubit Rz rotations, as shown in Eqs. (13)-(15) in [Hayes 2023](https://arxiv.org/pdf/2306.11073). 15 Unsigned magnitude encoding is assumed. 16 17 Arguments: 18 --- 19 - **n** : *int* 20 21 Number of qubits in the system. 22 23 Returns: 24 --- 25 - **circuit** : *QuantumCircuit* 26 27 Implementation of the gate as a qiskit `QuantumCircuit`. 28 29 """ 30 qc = QuantumCircuit(n, name="Extract Phase") 31 qubits = list(range(n)) 32 33 for k in np.arange(0,n): 34 lam = 2.*np.pi*(2.**(k-n)) 35 qubit = k 36 qc.p(lam,qubits[qubit]) 37 38 # package as instruction 39 qc_inst = qc.to_instruction() 40 circuit = QuantumCircuit(n) 41 circuit.append(qc_inst, qubits) 42 43 return circuit 44 45def full_encode(n,m, weights_A_str, weights_p_str,L_A,L_p, real_p, repeat_params=None, state_vec_file=None, save=False, full_state_vec=False, no_UA=False,operators="QRQ"): 46 """ 47 48 Execute the quantum state preparation protocol using pre-trained QCNN weights. 49 50 Arguments: 51 --- 52 - **n** : *int* 53 54 Number of qubits in the input register. 55 56 - **m** : *int* 57 58 Number of qubits in the target register. 59 60 - **weights_A_str** : *str* 61 62 File path to storage location of amplitude encoding QCNN weights. 63 64 - **weights_p_str** : *str* 65 66 File path to storage location of phase encoding QCNN weights. 67 68 - **L_A** : *int* 69 70 Number of layers in the amplitude encoding QCNN. 71 72 - **L_p** : *int* 73 74 Number of layers in the phase encoding QCNN. 75 76 - **real_p** : *boolean* 77 78 Value of the `real` argument of the phase encoding QCNN. See `pqcprep.pqc_tools.generate_network()` for details. 79 80 - **repeat_params** : *boolean* 81 82 Value of the `repeat_params` argument of the phase encoding QCNN. See `pqcprep.pqc_tools.generate_network()` for details. Default is False. 83 84 - **state_vec_file** : *str*, *optional* 85 86 File path to store output state vector. 87 88 - **save** : *boolean* 89 90 If True, output statevector is saved at `state_vec_file`. Default is False. 91 92 - **full_state_vec** : *boolean* 93 94 If True, the full statevector is additionally returned, including non-cleared ancilla states. Default is False. 95 96 - **no_UA** : *boolean* 97 98 If True, do not apply the amplitude encoding gate $\hat{U}_A$ and instead apply a Hadamard transform on the input register. Default is False. 99 100 - **operators** : *str* 101 102 Which operators to apply to the registers. Options are `'QRQ'` for full phase extraction, `'RQ'` for partial phase extraction, and `'Q'` for function 103 evaluation only. Default is `'QRQ'`. 104 105 Returns: 106 --- 107 - **state_v** : *array_like* 108 109 Array representing the statevector of the input register, assuming a cleared target register. 110 111 - **state_v_full** : *array_like*, *optional* 112 113 Array representing the statevector of the input register for the various target register configurations. 114 Only returned if `full_state_vec` is True. 115 116 """ 117 118 # set up registers 119 input_register = QuantumRegister(n, "input") 120 target_register = QuantumRegister(m, "target") 121 circuit = QuantumCircuit(input_register, target_register) 122 123 # load weights 124 if type(weights_p_str) !=str: 125 weights_p=weights_p_str 126 else: 127 weights_p = np.load(weights_p_str) 128 129 if no_UA: 130 circuit.h(input_register) 131 else: 132 weights_A = np.load(weights_A_str) 133 # encode amplitudes 134 circuit.compose(A_generate_network(n, L_A), input_register, inplace=True) 135 circuit = circuit.assign_parameters(weights_A) 136 137 # evaluate function 138 qc = generate_network(n,m, L_p, real=real_p,repeat_params=repeat_params) 139 qc = qc.assign_parameters(weights_p) 140 inv_qc = qc.inverse() 141 circuit.compose(qc, [*input_register,*target_register], inplace=True) 142 143 if operators=="QRQ" or operators=="RQ": 144 # extract phases 145 circuit.compose(extract_phase(m),target_register, inplace=True) 146 elif operators != "Q": 147 raise ValueError("Unexpected value for 'operators'. Should be 'QRQ', 'RQ', or 'Q'.") 148 149 if operators=="QRQ": 150 # clear ancilla register 151 circuit.compose(inv_qc, [*input_register,*target_register], inplace=True) 152 153 # get resulting statevector 154 state_vector = get_state_vec(circuit).reshape((2**m,2**n)) 155 156 state_v = state_vector[0,:].flatten() 157 158 if save: 159 if state_vec_file==None: raise ValueError("Must provide file path to save output.") 160 161 # save to file 162 np.save(state_vec_file, state_v) 163 if full_state_vec: 164 np.save(state_vec_file+"full", state_vector) 165 166 return 0 167 else: 168 if full_state_vec: 169 return state_v, state_vector 170 else: 171 return state_v 172 173def phase_from_state(state_vec): 174 r""" 175 Extracts the phase, reduced to the interval $[0, 2 \pi]$, from a statevector. 176 177 Phases corresponding to near-zero amplitudes are set to zero ("phase blanking"). 178 179 Arguments: 180 --- 181 - **state_vec** : *array_like* 182 183 The complex state vector. 184 185 186 Returns: 187 ---- 188 - **phase** : *array_like* 189 190 The corresponding phase. 191 """ 192 amplitude = np.abs(state_vec) 193 phase = np.angle(state_vec) + 2* np.pi * (np.angle(state_vec) < 0).astype(int) 194 phase = np.angle(state_vec) - 2* np.pi * (np.angle(state_vec) > 2*np.pi).astype(int) 195 phase *= (amplitude > 1e-15).astype(float) 196 197 return phase
9def extract_phase(n): 10 r""" 11 12 Constructs an operator that extracts the phase associated with a given computational basis state: 13 $ \ket{k} \mapsto e^{2 \pi k} \ket{k}$. 14 15 This is based on a sequence of single-qubit Rz rotations, as shown in Eqs. (13)-(15) in [Hayes 2023](https://arxiv.org/pdf/2306.11073). 16 Unsigned magnitude encoding is assumed. 17 18 Arguments: 19 --- 20 - **n** : *int* 21 22 Number of qubits in the system. 23 24 Returns: 25 --- 26 - **circuit** : *QuantumCircuit* 27 28 Implementation of the gate as a qiskit `QuantumCircuit`. 29 30 """ 31 qc = QuantumCircuit(n, name="Extract Phase") 32 qubits = list(range(n)) 33 34 for k in np.arange(0,n): 35 lam = 2.*np.pi*(2.**(k-n)) 36 qubit = k 37 qc.p(lam,qubits[qubit]) 38 39 # package as instruction 40 qc_inst = qc.to_instruction() 41 circuit = QuantumCircuit(n) 42 circuit.append(qc_inst, qubits) 43 44 return circuit
Constructs an operator that extracts the phase associated with a given computational basis state: $ \ket{k} \mapsto e^{2 \pi k} \ket{k}$.
This is based on a sequence of single-qubit Rz rotations, as shown in Eqs. (13)-(15) in Hayes 2023. Unsigned magnitude encoding is assumed.
Arguments:
n : int
Number of qubits in the system.
Returns:
circuit : QuantumCircuit
Implementation of the gate as a qiskit
QuantumCircuit.
46def full_encode(n,m, weights_A_str, weights_p_str,L_A,L_p, real_p, repeat_params=None, state_vec_file=None, save=False, full_state_vec=False, no_UA=False,operators="QRQ"): 47 """ 48 49 Execute the quantum state preparation protocol using pre-trained QCNN weights. 50 51 Arguments: 52 --- 53 - **n** : *int* 54 55 Number of qubits in the input register. 56 57 - **m** : *int* 58 59 Number of qubits in the target register. 60 61 - **weights_A_str** : *str* 62 63 File path to storage location of amplitude encoding QCNN weights. 64 65 - **weights_p_str** : *str* 66 67 File path to storage location of phase encoding QCNN weights. 68 69 - **L_A** : *int* 70 71 Number of layers in the amplitude encoding QCNN. 72 73 - **L_p** : *int* 74 75 Number of layers in the phase encoding QCNN. 76 77 - **real_p** : *boolean* 78 79 Value of the `real` argument of the phase encoding QCNN. See `pqcprep.pqc_tools.generate_network()` for details. 80 81 - **repeat_params** : *boolean* 82 83 Value of the `repeat_params` argument of the phase encoding QCNN. See `pqcprep.pqc_tools.generate_network()` for details. Default is False. 84 85 - **state_vec_file** : *str*, *optional* 86 87 File path to store output state vector. 88 89 - **save** : *boolean* 90 91 If True, output statevector is saved at `state_vec_file`. Default is False. 92 93 - **full_state_vec** : *boolean* 94 95 If True, the full statevector is additionally returned, including non-cleared ancilla states. Default is False. 96 97 - **no_UA** : *boolean* 98 99 If True, do not apply the amplitude encoding gate $\hat{U}_A$ and instead apply a Hadamard transform on the input register. Default is False. 100 101 - **operators** : *str* 102 103 Which operators to apply to the registers. Options are `'QRQ'` for full phase extraction, `'RQ'` for partial phase extraction, and `'Q'` for function 104 evaluation only. Default is `'QRQ'`. 105 106 Returns: 107 --- 108 - **state_v** : *array_like* 109 110 Array representing the statevector of the input register, assuming a cleared target register. 111 112 - **state_v_full** : *array_like*, *optional* 113 114 Array representing the statevector of the input register for the various target register configurations. 115 Only returned if `full_state_vec` is True. 116 117 """ 118 119 # set up registers 120 input_register = QuantumRegister(n, "input") 121 target_register = QuantumRegister(m, "target") 122 circuit = QuantumCircuit(input_register, target_register) 123 124 # load weights 125 if type(weights_p_str) !=str: 126 weights_p=weights_p_str 127 else: 128 weights_p = np.load(weights_p_str) 129 130 if no_UA: 131 circuit.h(input_register) 132 else: 133 weights_A = np.load(weights_A_str) 134 # encode amplitudes 135 circuit.compose(A_generate_network(n, L_A), input_register, inplace=True) 136 circuit = circuit.assign_parameters(weights_A) 137 138 # evaluate function 139 qc = generate_network(n,m, L_p, real=real_p,repeat_params=repeat_params) 140 qc = qc.assign_parameters(weights_p) 141 inv_qc = qc.inverse() 142 circuit.compose(qc, [*input_register,*target_register], inplace=True) 143 144 if operators=="QRQ" or operators=="RQ": 145 # extract phases 146 circuit.compose(extract_phase(m),target_register, inplace=True) 147 elif operators != "Q": 148 raise ValueError("Unexpected value for 'operators'. Should be 'QRQ', 'RQ', or 'Q'.") 149 150 if operators=="QRQ": 151 # clear ancilla register 152 circuit.compose(inv_qc, [*input_register,*target_register], inplace=True) 153 154 # get resulting statevector 155 state_vector = get_state_vec(circuit).reshape((2**m,2**n)) 156 157 state_v = state_vector[0,:].flatten() 158 159 if save: 160 if state_vec_file==None: raise ValueError("Must provide file path to save output.") 161 162 # save to file 163 np.save(state_vec_file, state_v) 164 if full_state_vec: 165 np.save(state_vec_file+"full", state_vector) 166 167 return 0 168 else: 169 if full_state_vec: 170 return state_v, state_vector 171 else: 172 return state_v
Execute the quantum state preparation protocol using pre-trained QCNN weights.
Arguments:
n : int
Number of qubits in the input register.
m : int
Number of qubits in the target register.
weights_A_str : str
File path to storage location of amplitude encoding QCNN weights.
weights_p_str : str
File path to storage location of phase encoding QCNN weights.
L_A : int
Number of layers in the amplitude encoding QCNN.
L_p : int
Number of layers in the phase encoding QCNN.
real_p : boolean
Value of the
realargument of the phase encoding QCNN. Seepqcprep.pqc_tools.generate_network()for details.repeat_params : boolean
Value of the
repeat_paramsargument of the phase encoding QCNN. Seepqcprep.pqc_tools.generate_network()for details. Default is False.state_vec_file : str, optional
File path to store output state vector.
save : boolean
If True, output statevector is saved at
state_vec_file. Default is False.full_state_vec : boolean
If True, the full statevector is additionally returned, including non-cleared ancilla states. Default is False.
no_UA : boolean
If True, do not apply the amplitude encoding gate $\hat{U}_A$ and instead apply a Hadamard transform on the input register. Default is False.
operators : str
Which operators to apply to the registers. Options are
'QRQ'for full phase extraction,'RQ'for partial phase extraction, and'Q'for function evaluation only. Default is'QRQ'.
Returns:
state_v : array_like
Array representing the statevector of the input register, assuming a cleared target register.
state_v_full : array_like, optional
Array representing the statevector of the input register for the various target register configurations.
Only returned iffull_state_vecis True.
174def phase_from_state(state_vec): 175 r""" 176 Extracts the phase, reduced to the interval $[0, 2 \pi]$, from a statevector. 177 178 Phases corresponding to near-zero amplitudes are set to zero ("phase blanking"). 179 180 Arguments: 181 --- 182 - **state_vec** : *array_like* 183 184 The complex state vector. 185 186 187 Returns: 188 ---- 189 - **phase** : *array_like* 190 191 The corresponding phase. 192 """ 193 amplitude = np.abs(state_vec) 194 phase = np.angle(state_vec) + 2* np.pi * (np.angle(state_vec) < 0).astype(int) 195 phase = np.angle(state_vec) - 2* np.pi * (np.angle(state_vec) > 2*np.pi).astype(int) 196 phase *= (amplitude > 1e-15).astype(float) 197 198 return phase
Extracts the phase, reduced to the interval $[0, 2 \pi]$, from a statevector.
Phases corresponding to near-zero amplitudes are set to zero ("phase blanking").
Arguments:
state_vec : array_like
The complex state vector.
Returns:
phase : array_like
The corresponding phase.