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 
def extract_phase(n):
 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.

def 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'):
 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 real argument of the phase encoding QCNN. See pqcprep.pqc_tools.generate_network() for details.

  • repeat_params : boolean

    Value of the repeat_params argument of the phase encoding QCNN. See pqcprep.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 if full_state_vec is True.

def phase_from_state(state_vec):
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.