4 Created on Tue Jun 30 15:44:26 2020 5 Copyright 2020 Peter Rakyta, Ph.D. 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 11 http://www.apache.org/licenses/LICENSE-2.0 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions andP 17 limitations under the License. 19 You should have received a copy of the GNU General Public License 20 along with this program. If not, see http://www.gnu.org/licenses/. 22 @author: Peter Rakyta, Ph.D. 25 """A Python interface class representing Squander circuit.""" 29 from squander.gates.qgd_Circuit_Wrapper
import qgd_Circuit_Wrapper
32 from squander.gates.gates_Wrapper
import (
71 """A QGD Python interface class for the Gates_Block.""" 74 """Constructor of the class. 77 qbit_num: The number of qubits spanning the operations 84 """Add a U1 gate to the front of the gate structure. 87 target_qbit: Target qubit index (int) 91 super().
add_U1(target_qbit)
94 """Add a U2 gate to the front of the gate structure. 97 target_qbit: Target qubit index (int) 101 super().
add_U2(target_qbit)
104 """Add a U3 gate to the front of the gate structure. 107 target_qbit: Target qubit index (int) 111 super().
add_U3(target_qbit)
114 """Add a RX gate to the front of the gate structure. 117 target_qbit: Target qubit index (int) 121 super().
add_RX(target_qbit)
124 """Add a R gate to the front of the gate structure. 127 target_qbit: Target qubit index (int) 131 super().
add_R(target_qbit)
134 """Add a RY gate to the front of the gate structure. 137 target_qbit: Target qubit index (int) 141 super().
add_RY(target_qbit)
144 """Add a RZ gate to the front of the gate structure. 147 target_qbit: Target qubit index (int) 151 super().
add_RZ(target_qbit)
154 """Add a CNOT gate to the front of the gate structure. 157 target_qbit: Target qubit index (int) 158 control_qbit: Control qubit index (int) 162 super().
add_CNOT(target_qbit, control_qbit)
164 def add_CZ(self, target_qbit, control_qbit):
165 """Add a CZ gate to the front of the gate structure. 168 target_qbit: Target qubit index (int) 169 control_qbit: Control qubit index (int) 173 super().
add_CZ(target_qbit, control_qbit)
175 def add_CH(self, target_qbit, control_qbit):
176 """Add a CH gate to the front of the gate structure. 179 target_qbit: Target qubit index (int) 180 control_qbit: Control qubit index (int) 184 super().
add_CH(target_qbit, control_qbit)
186 def add_CU(self, target_qbit, control_qbit):
187 """Add a CU gate to the front of the gate structure. 190 target_qbit: Target qubit index (int) 191 control_qbit: Control qubit index (int) 195 super().
add_CU(target_qbit, control_qbit)
198 """Add a SYC gate to the front of the gate structure. 201 target_qbit: Target qubit index (int) 202 control_qbit: Control qubit index (int) 206 super().
add_SYC(target_qbit, control_qbit)
209 """Add a Hadamard gate to the front of the gate structure. 212 target_qbit: Target qubit index (int) 216 super().
add_H(target_qbit)
219 """Add a X gate to the front of the gate structure. 222 target_qbit: Target qubit index (int) 226 super().
add_X(target_qbit)
229 """Add a Y gate to the front of the gate structure. 232 target_qbit: Target qubit index (int) 236 super().
add_Y(target_qbit)
239 """Add a Z gate to the front of the gate structure. 242 target_qbit: Target qubit index (int) 246 super().
add_Z(target_qbit)
249 """Add a SX gate to the front of the gate structure. 252 target_qbit: Target qubit index (int) 256 super().
add_SX(target_qbit)
259 """Add a SXdg gate to the front of the gate structure. 262 target_qbit: Target qubit index (int) 269 """Add a S gate to the front of the gate structure. 272 target_qbit: Target qubit index (int) 276 super().
add_S(target_qbit)
279 """Add a Sdg gate to the front of the gate structure. 282 target_qbit: Target qubit index (int) 289 """Add a T gate to the front of the gate structure. 292 target_qbit: Target qubit index (int) 296 super().
add_T(target_qbit)
299 """Add a Tdg gate to the front of the gate structure. 302 target_qbit: Target qubit index (int) 309 """Add an adaptive gate to the front of the gate structure. 312 target_qbit: Target qubit index (int) 313 control_qbit: Control qubit index (int) 320 """Add a CROT gate to the front of the gate structure. 323 target_qbit: Target qubit index (int) 324 control_qbit: Control qubit index (int) 328 super(qgd_Circuit, self).
add_CROT(target_qbit, control_qbit)
330 def add_CR(self, target_qbit, control_qbit):
331 """Add a CR gate to the front of the gate structure. 334 target_qbit: Target qubit index (int) 335 control_qbit: Control qubit index (int) 339 super(qgd_Circuit, self).
add_CR(target_qbit, control_qbit)
342 """Add a CRY gate to the front of the gate structure. 345 target_qbit: Target qubit index (int) 346 control_qbit: Control qubit index (int) 350 super(qgd_Circuit, self).
add_CRY(target_qbit, control_qbit)
353 """Add a CRZ gate to the front of the gate structure. 356 target_qbit: Target qubit index (int) 357 control_qbit: Control qubit index (int) 361 super(qgd_Circuit, self).
add_CRZ(target_qbit, control_qbit)
364 """Add a CRX gate to the front of the gate structure. 367 target_qbit: Target qubit index (int) 368 control_qbit: Control qubit index (int) 372 super(qgd_Circuit, self).
add_CRX(target_qbit, control_qbit)
374 def add_CP(self, target_qbit, control_qbit):
375 """Add a CP gate to the front of the gate structure. 378 target_qbit: Target qubit index (int) 379 control_qbit: Control qubit index (int) 383 super(qgd_Circuit, self).
add_CP(target_qbit, control_qbit)
386 """Add a SWAP gate to the front of the gate structure. 389 target_qbits: List of target qubits (list of int) - at least 2 qubits required 390 target_qbit2: Optional second qubit if target_qbits is a single int 393 if isinstance(target_qbits, (list, tuple)):
394 super(qgd_Circuit, self).
add_SWAP(list(target_qbits))
395 if isinstance(target_qbits, int)
and target_qbit2 != -1:
396 super(qgd_Circuit, self).
add_SWAP(list([target_qbits, target_qbit2]))
398 def add_RXX(self, target_qbits, target_qbit2=-1):
399 """Add a RXX gate to the front of the gate structure. 402 target_qbits: List of target qubits (list of int) - at least 2 qubits required 403 target_qbit2: Optional second qubit if target_qbits is a single int 406 if isinstance(target_qbits, (list, tuple)):
407 super(qgd_Circuit, self).
add_RXX(list(target_qbits))
408 if isinstance(target_qbits, int)
and target_qbit2 != -1:
409 super(qgd_Circuit, self).
add_RXX(list([target_qbits, target_qbit2]))
411 def add_RYY(self, target_qbits, target_qbit2=-1):
412 """Add a RYY gate to the front of the gate structure. 415 target_qbits: List of target qubits (list of int) - at least 2 qubits required 416 target_qbit2: Optional second qubit if target_qbits is a single int 419 if isinstance(target_qbits, (list, tuple)):
420 super(qgd_Circuit, self).
add_RYY(list(target_qbits))
421 if isinstance(target_qbits, int)
and target_qbit2 != -1:
422 super(qgd_Circuit, self).
add_RYY(list([target_qbits, target_qbit2]))
424 def add_RZZ(self, target_qbits, target_qbit2=-1):
425 """Add a RZZ gate to the front of the gate structure. 428 target_qbits: List of target qubits (list of int) - at least 2 qubits required 429 target_qbit2: Optional second qubit if target_qbits is a single int 432 if isinstance(target_qbits, (list, tuple)):
433 super(qgd_Circuit, self).
add_RZZ(list(target_qbits))
434 if isinstance(target_qbits, int)
and target_qbit2 != -1:
435 super(qgd_Circuit, self).
add_RZZ(list([target_qbits, target_qbit2]))
438 """Add a CSWAP (Fredkin) gate to the front of the gate structure. 441 target_qbits: Target qubits (list of int) - exactly 2 for standard CSWAP 442 control_qbits: Control qubit(s) (int or list of int) - exactly 1 for standard CSWAP 445 Accepts both list and single integer inputs for control_qbits. Examples: 446 - add_CSWAP([0,1], 2) -> control_qbits becomes [2] (standard Fredkin gate) 447 - add_CSWAP([0,1], [2]) -> control_qbits stays [2] (standard Fredkin gate) 448 Currently only supports exactly 1 control qubit (standard Fredkin gate). 451 if not isinstance(target_qbits, (list, tuple)):
452 raise TypeError(
"target_qbits must be a list or tuple")
453 target_qbits = list(target_qbits)
456 if isinstance(control_qbits, int):
457 control_qbits = [control_qbits]
458 elif isinstance(control_qbits, (list, tuple)):
459 control_qbits = list(control_qbits)
461 raise TypeError(
"control_qbits must be an int, list, or tuple")
464 super(qgd_Circuit, self).
add_CSWAP(target_qbits, control_qbits)
466 def add_CCX(self, target_qbit, control_qbits):
467 """Add a CCX gate to the front of the gate structure. 470 target_qbit: Target qubit index (int) 471 control_qbits: Control qubits (list of int or tuple) - at least 2 control qubits required 474 control_qbits can be a list or tuple. Example: 475 - add_CCX(0, [1,2]) -> standard CCX with 2 controls 478 if isinstance(control_qbits, int):
480 "control_qbits must be a list or tuple (CCX requires at least 2 control qubits)" 482 elif isinstance(control_qbits, (list, tuple)):
483 control_qbits = list(control_qbits)
485 raise TypeError(
"control_qbits must be a list or tuple")
488 super(qgd_Circuit, self).
add_CCX(target_qbit, control_qbits)
491 """Add a block of operations (subcircuit) to the front of the gate structure. 494 gate: A qgd_Circuit instance representing the subcircuit to add 500 def add_GENERAL(self, operation_mtx, target_qbits=None, control_qbits=None, is_f32=False):
501 """Add a GENERAL_OPERATION gate from an explicit unitary matrix. 504 operation_mtx: Square operation matrix with shape (2**qbit_num, 2**qbit_num) 505 target_qbits: Optional list of target qubits used as metadata 506 control_qbits: Optional list of control qubits used as metadata 507 is_f32: If True, interpret operation_mtx as complex64 510 if target_qbits
is None:
512 if control_qbits
is None:
515 super().
add_GENERAL(operation_mtx, target_qbits, control_qbits, is_f32=is_f32)
518 """Retrieve the matrix representation of the circuit operation. 521 parameters_mtx: Parameter array (numpy array) for parametric gates 522 is_f32: If True, use float32 precision (default False) 525 numpy.ndarray: The matrix representation of the circuit 528 parameters_mtx = np.asarray(
530 dtype=np.float32
if is_f32
else np.float64,
534 return super().
get_Matrix(parameters_mtx, is_f32=is_f32)
537 """Get the number of free parameters in the gate structure. 540 int: The number of free parameters 546 def apply_to(self, parameters_mtx, unitary_mtx, parallel=1, is_f32=False):
547 """Apply the gate circuit operation on the input matrix. 550 parameters_mtx: Parameter array (numpy array) for parametric gates 551 unitary_mtx: Input matrix (numpy array) to be transformed 552 parallel: Parallel execution mode (int, optional, default=1) 553 is_f32: Use float32/complex64 precision (bool, optional, default=False) 557 super().
apply_to(parameters_mtx, unitary_mtx, parallel=parallel, is_f32=is_f32)
560 """Apply the gate circuit from the right on the input matrix. 563 parameters_mtx: Parameter array (numpy array) for parametric gates 564 unitary_mtx: Input matrix (numpy array) to be transformed 565 parallel: Parallel execution mode (int, optional, default=1) 566 is_f32: Use float32/complex64 precision (bool, optional, default=False) 570 super().
apply_from_right(parameters_mtx, unitary_mtx, parallel=parallel, is_f32=is_f32)
573 """Apply the circuit to a list of input matrices with float32/float64 dispatch. 576 inputs: List of numpy arrays to transform in-place (complex128 or complex64 when is_f32=True) 577 parameters_mtx: Parameter array (float64 or float32 when is_f32=True) 578 parallel: Parallel execution mode (int, optional, default=1) 579 is_f32: Use float32/complex64 precision (bool, optional, default=False) 581 super().
apply_to_list(inputs, parameters_mtx, parallel, is_f32=is_f32)
584 """Evaluate the derivative of the circuit on an input matrix w.r.t. all free parameters. 587 parameters_mtx: Parameter array (float64 or float32 when is_f32=True) 588 unitary_mtx: Input matrix (complex128 or complex64 when is_f32=True) 589 parallel: Parallel execution mode (int, optional, default=1) 590 is_f32: Use float32/complex64 precision (bool, optional, default=False) 593 list of numpy arrays: One matrix per free parameter (complex128 or complex64 when is_f32=True) 595 return super().
apply_derivate_to(parameters_mtx, unitary_mtx, parallel, is_f32=is_f32)
598 """Evaluate forward circuit action and all derivatives in one call. 600 Return format is a list where element 0 is the forward apply_to result, 601 and elements 1..N are derivatives w.r.t. each free parameter. 604 parameters_mtx: Parameter array (float64 or float32 when is_f32=True) 605 unitary_mtx: Input matrix (complex128 or complex64 when is_f32=True) 606 parallel: Parallel execution mode (int, optional, default=1) 607 is_f32: Use float32/complex64 precision (bool, optional, default=False) 610 list of numpy arrays: [forward_output, derivative_0, derivative_1, ...] 612 return super().
apply_to_combined(parameters_mtx, unitary_mtx, parallel, is_f32=is_f32)
615 self, parameters=None, input_state=None, qubit_list=None
617 """Calculate the second Rényi entropy of the quantum circuit. 620 parameters: Parameter array (float64 numpy array, optional) 621 input_state: Input quantum state (complex numpy array, optional). If None, |0> is created 622 qubit_list: Subset of qubits for which the Rényi entropy should be calculated (list, optional) 625 float: The calculated second Rényi entropy 632 qubit_list_validated = list()
633 if isinstance(qubit_list, list)
or isinstance(qubit_list, tuple):
634 for item
in qubit_list:
635 if isinstance(item, int):
636 qubit_list_validated.append(item)
637 qubit_list_validated = list(set(qubit_list_validated))
639 print(
"Elements of qbit_list should be integers")
641 elif qubit_list ==
None:
642 qubit_list_validated = [x
for x
in range(qbit_num)]
645 print(
"Elements of qbit_list should be integers")
648 if parameters
is None:
649 print(
"get_Second_Renyi_entropy: array of input parameters is None")
652 if input_state
is None:
653 matrix_size = 1 << qbit_num
654 input_state = np.zeros((matrix_size, 1), dtype=np.complex128)
659 parameters, input_state, qubit_list_validated
665 """Get the number of qubits in the circuit. 668 int: The number of qubits 674 """Get the list of qubits involved in the circuit. 677 list: List of qubit indices involved in the circuit 683 """Set the minimum fusion parameter in the circuit. 686 min_fusion: Minimum fusion value (int) 692 """Get the list of gates (or subcircuits) in the circuit. 695 list: List of gate objects in the circuit 701 """Get statistics on the gate counts in the circuit. 704 dict: Dictionary containing the gate type counts 710 """Remap the qubits in the circuit. 713 qbit_map: Dictionary mapping initial qubit indices to remapped qubit indices 714 Format: {int(initial_qbit): int(remapped_qbit)} 715 qbit_num: Number of qubits in the remapped circuit (int, optional). 716 Can be different from the original circuit. If None, uses the original number 719 qgd_Circuit: A newly created, remapped circuit instance 728 """Get the starting index of the parameters in the parameter array. 730 The starting index corresponds to the circuit in which the current gate is incorporated. 733 int: The starting index of parameters 740 """Get the list of parent gate indices. 742 The parent gates can be obtained from the list of gates involved in the circuit. 745 gate: Gate index (int) for which to retrieve parent gates 748 list: List of parent gate indices 755 """Get the list of child gate indices. 757 The children gates can be obtained from the list of gates involved in the circuit. 760 gate: Gate index (int) for which to retrieve child gates 763 list: List of child gate indices 770 """Add a generic gate to the circuit. 773 qgd_gate: A gate object from the gates_Wrapper module 776 Exception: If the gate type is not implemented 779 if isinstance(qgd_gate, H):
780 self.
add_H(qgd_gate.get_Target_Qbit())
781 elif isinstance(qgd_gate, X):
782 self.
add_X(qgd_gate.get_Target_Qbit())
783 elif isinstance(qgd_gate, Y):
784 self.
add_Y(qgd_gate.get_Target_Qbit())
785 elif isinstance(qgd_gate, Z):
786 self.
add_Z(qgd_gate.get_Target_Qbit())
787 elif isinstance(qgd_gate, CH):
788 self.
add_CH(qgd_gate.get_Target_Qbit(), qgd_gate.get_Control_Qbit())
789 elif isinstance(qgd_gate, CZ):
790 self.
add_CZ(qgd_gate.get_Target_Qbit(), qgd_gate.get_Control_Qbit())
791 elif isinstance(qgd_gate, RX):
792 self.
add_RX(qgd_gate.get_Target_Qbit())
793 elif isinstance(qgd_gate, RY):
794 self.
add_RY(qgd_gate.get_Target_Qbit())
795 elif isinstance(qgd_gate, RZ):
796 self.
add_RZ(qgd_gate.get_Target_Qbit())
797 elif isinstance(qgd_gate, SX):
798 self.
add_SX(qgd_gate.get_Target_Qbit())
799 elif isinstance(qgd_gate, SXdg):
800 self.
add_SXdg(qgd_gate.get_Target_Qbit())
801 elif isinstance(qgd_gate, U1):
802 self.
add_U1(qgd_gate.get_Target_Qbit())
803 elif isinstance(qgd_gate, U2):
804 self.
add_U2(qgd_gate.get_Target_Qbit())
805 elif isinstance(qgd_gate, U3):
806 self.
add_U3(qgd_gate.get_Target_Qbit())
807 elif isinstance(qgd_gate, CRY):
808 self.
add_CRY(qgd_gate.get_Target_Qbit(), qgd_gate.get_Control_Qbit())
809 elif isinstance(qgd_gate, CNOT):
810 self.
add_CNOT(qgd_gate.get_Target_Qbit(), qgd_gate.get_Control_Qbit())
811 elif isinstance(qgd_gate, S):
812 self.
add_S(qgd_gate.get_Target_Qbit())
813 elif isinstance(qgd_gate, Sdg):
814 self.
add_Sdg(qgd_gate.get_Target_Qbit())
815 elif isinstance(qgd_gate, T):
816 self.
add_T(qgd_gate.get_Target_Qbit())
817 elif isinstance(qgd_gate, Tdg):
818 self.
add_Tdg(qgd_gate.get_Target_Qbit())
819 elif isinstance(qgd_gate, R):
820 self.
add_R(qgd_gate.get_Target_Qbit())
821 elif isinstance(qgd_gate, CROT):
822 self.
add_CROT(qgd_gate.get_Target_Qbit(), qgd_gate.get_Control_Qbit())
823 elif isinstance(qgd_gate, CR):
824 self.
add_CR(qgd_gate.get_Target_Qbit(), qgd_gate.get_Control_Qbit())
825 elif isinstance(qgd_gate, SYC):
826 self.
add_SYC(qgd_gate.get_Target_Qbit(), qgd_gate.get_Control_Qbit())
827 elif isinstance(qgd_gate, CRZ):
828 self.
add_CRZ(qgd_gate.get_Target_Qbit(), qgd_gate.get_Control_Qbit())
829 elif isinstance(qgd_gate, CRX):
830 self.
add_CRX(qgd_gate.get_Target_Qbit(), qgd_gate.get_Control_Qbit())
831 elif isinstance(qgd_gate, CP):
832 self.
add_CP(qgd_gate.get_Target_Qbit(), qgd_gate.get_Control_Qbit())
833 elif isinstance(qgd_gate, CU):
834 self.
add_CU(qgd_gate.get_Target_Qbit(), qgd_gate.get_Control_Qbit())
835 elif isinstance(qgd_gate, SWAP):
836 self.
add_SWAP(qgd_gate.get_Target_Qbits())
837 elif isinstance(qgd_gate, RXX):
838 self.
add_RXX(qgd_gate.get_Target_Qbits())
839 elif isinstance(qgd_gate, RYY):
840 self.
add_RYY(qgd_gate.get_Target_Qbits())
841 elif isinstance(qgd_gate, RZZ):
842 self.
add_RZZ(qgd_gate.get_Target_Qbits())
843 elif isinstance(qgd_gate, CSWAP):
844 self.
add_CSWAP(qgd_gate.get_Target_Qbits(), qgd_gate.get_Control_Qbits())
845 elif isinstance(qgd_gate, CCX):
846 self.
add_CCX(qgd_gate.get_Target_Qbit(), qgd_gate.get_Control_Qbits())
848 raise Exception(
"Cannot add gate: unimplemented gate type")
def apply_to(self, parameters_mtx, unitary_mtx, parallel=1, is_f32=False)
def add_T(self, target_qbit)
def add_H(self, target_qbit)
def add_Circuit(self, gate)
def add_GENERAL(self, operation_mtx, target_qbits=None, control_qbits=None, is_f32=False)
def apply_to_combined(self, parameters_mtx, unitary_mtx, parallel=1, is_f32=False)
def apply_from_right(self, parameters_mtx, unitary_mtx, parallel=1, is_f32=False)
def add_RY(self, target_qbit)
def add_SWAP(self, target_qbits, target_qbit2=-1)
def add_X(self, target_qbit)
def Remap_Qbits(self, qbit_map, qbit_num=None)
def add_CH(self, target_qbit, control_qbit)
def add_CNOT(self, target_qbit, control_qbit)
def add_RYY(self, target_qbits, target_qbit2=-1)
def apply_derivate_to(self, parameters_mtx, unitary_mtx, parallel=1, is_f32=False)
def set_min_fusion(self, min_fusion)
def get_Matrix(self, parameters_mtx, is_f32=False)
def add_CRY(self, target_qbit, control_qbit)
def add_RZ(self, target_qbit)
def add_Sdg(self, target_qbit)
def add_CCX(self, target_qbit, control_qbits)
def add_Gate(self, qgd_gate)
def add_CRX(self, target_qbit, control_qbit)
def add_S(self, target_qbit)
def add_Z(self, target_qbit)
def add_Tdg(self, target_qbit)
def add_RXX(self, target_qbits, target_qbit2=-1)
def add_CR(self, target_qbit, control_qbit)
def add_CSWAP(self, target_qbits, control_qbits)
def add_R(self, target_qbit)
def add_RZZ(self, target_qbits, target_qbit2=-1)
def add_U2(self, target_qbit)
def add_CROT(self, target_qbit, control_qbit)
def get_Parents(self, gate)
def get_Children(self, gate)
def add_SXdg(self, target_qbit)
def apply_to_list(self, inputs, parameters_mtx, parallel=1, is_f32=False)
def add_CP(self, target_qbit, control_qbit)
def add_RX(self, target_qbit)
def get_Second_Renyi_Entropy(self, parameters=None, input_state=None, qubit_list=None)
def __init__(self, qbit_num)
def add_SYC(self, target_qbit, control_qbit)
def add_U1(self, target_qbit)
def get_Parameter_Start_Index(self)
def add_Y(self, target_qbit)
def get_Parameter_Num(self)
def add_U3(self, target_qbit)
def add_CZ(self, target_qbit, control_qbit)
def add_adaptive(self, target_qbit, control_qbit)
def add_CU(self, target_qbit, control_qbit)
def add_SX(self, target_qbit)
def add_CRZ(self, target_qbit, control_qbit)