def _circuit_xyx(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): gphase = phase - (phi + lam) / 2 qr = QuantumRegister(1, "qr") circuit = QuantumCircuit(qr) if not simplify: atol = -1.0 if abs(theta) < atol: tot = _mod_2pi(phi + lam, atol) if abs(tot) > atol: circuit._append(RXGate(tot), [qr[0]], []) gphase += tot / 2 circuit.global_phase = gphase return circuit if abs(theta - np.pi) < atol: gphase += phi lam, phi = lam - phi, 0 lam = _mod_2pi(lam, atol) if abs(lam) > atol: gphase += lam / 2 circuit._append(RXGate(lam), [qr[0]], []) circuit._append(RYGate(theta), [qr[0]], []) phi = _mod_2pi(phi, atol) if abs(phi) > atol: gphase += phi / 2 circuit._append(RXGate(phi), [qr[0]], []) circuit.global_phase = gphase return circuit
def _circuit_u1x(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): # Shift theta and phi so decomposition is # U1(phi).X90.U1(theta).X90.U1(lam) theta += np.pi phi += np.pi # Check for decomposition into minimimal number required X90 pulses if simplify and np.isclose(abs(theta), np.pi, atol=atol): # Zero X90 gate decomposition circuit = QuantumCircuit(1, global_phase=phase) circuit.append(U1Gate(lam + phi + theta), [0]) return circuit if simplify and np.isclose(abs(theta), np.pi / 2, atol=atol): # Single X90 gate decomposition circuit = QuantumCircuit(1, global_phase=phase) circuit.append(U1Gate(lam + theta), [0]) circuit.append(RXGate(np.pi / 2), [0]) circuit.append(U1Gate(phi + theta), [0]) return circuit # General two-X90 gate decomposition circuit = QuantumCircuit(1, global_phase=phase) circuit.append(U1Gate(lam), [0]) circuit.append(RXGate(np.pi / 2), [0]) circuit.append(U1Gate(theta), [0]) circuit.append(RXGate(np.pi / 2), [0]) circuit.append(U1Gate(phi), [0]) return circuit
def specialize(self): self.b = self.c = (self.b + self.c) / 2 k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_xyx.angles_and_phase(self.K2l) self.global_phase += k2lphase self.K1r = self.K1r @ np.asarray(RXGate(k2lphi)) self.K1l = self.K1l @ np.asarray(RXGate(k2lphi)) self.K2l = np.asarray(RYGate(k2ltheta)) @ np.asarray(RXGate(k2llambda)) self.K2r = np.asarray(RXGate(-k2lphi)) @ self.K2r
def __init__(self): super().__init__( None, name="FakeV2", description="A fake BackendV2 example", online_date=datetime.datetime.utcnow(), backend_version="0.0.1", ) self._target = Target() self._theta = Parameter("theta") self._phi = Parameter("phi") self._lam = Parameter("lambda") rx_props = { (0, ): InstructionProperties(duration=5.23e-8, error=0.00038115), (1, ): InstructionProperties(duration=4.52e-8, error=0.00032115), } self._target.add_instruction(RXGate(self._theta), rx_props) rx_30_props = { (0, ): InstructionProperties(duration=1.23e-8, error=0.00018115), (1, ): InstructionProperties(duration=1.52e-8, error=0.00012115), } self._target.add_instruction(RXGate(np.pi / 6), rx_30_props, name="rx_30") u_props = { (0, ): InstructionProperties(duration=5.23e-8, error=0.00038115), (1, ): InstructionProperties(duration=4.52e-8, error=0.00032115), } self._target.add_instruction(UGate(self._theta, self._phi, self._lam), u_props) cx_props = { (0, 1): InstructionProperties(duration=5.23e-7, error=0.00098115), } self._target.add_instruction(CXGate(), cx_props) measure_props = { (0, ): InstructionProperties(duration=6e-6, error=5e-6), (1, ): InstructionProperties(duration=1e-6, error=9e-6), } self._target.add_instruction(Measure(), measure_props) ecr_props = { (1, 0): InstructionProperties(duration=4.52e-9, error=0.0000132115), } self._target.add_instruction(ECRGate(), ecr_props) self.options.set_validator("shots", (1, 4096)) self._qubit_properties = { 0: QubitProperties(t1=63.48783e-6, t2=112.23246e-6, frequency=5.17538e9), 1: QubitProperties(t1=73.09352e-6, t2=126.83382e-6, frequency=5.26722e9), }
def _circuit_xyx(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): circuit = QuantumCircuit(1, global_phase=phase) if simplify and np.isclose(theta, 0.0, atol=atol): circuit.append(RXGate(phi + lam), [0]) return circuit if not simplify or not np.isclose(lam, 0.0, atol=atol): circuit.append(RXGate(lam), [0]) if not simplify or not np.isclose(theta, 0.0, atol=atol): circuit.append(RYGate(theta), [0]) if not simplify or not np.isclose(phi, 0.0, atol=atol): circuit.append(RXGate(phi), [0]) return circuit
def _circuit_xyx(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): qr = QuantumRegister(1, 'qr') circuit = QuantumCircuit(qr, global_phase=phase) if simplify and math.isclose(theta, 0.0, abs_tol=atol): circuit._append(RXGate(phi + lam), [qr[0]], []) return circuit if not simplify or not math.isclose(lam, 0.0, abs_tol=atol): circuit._append(RXGate(lam), [qr[0]], []) if not simplify or not math.isclose(theta, 0.0, abs_tol=atol): circuit._append(RYGate(theta), [qr[0]], []) if not simplify or not math.isclose(phi, 0.0, abs_tol=atol): circuit._append(RXGate(phi), [qr[0]], []) return circuit
def run(self, dag): runs = dag.collect_runs([self.gate]) runs = _split_runs_on_parameters(runs) for group in runs: angle = sum([float(node.op.params[0]) for node in group]) angle = sign(angle) * (abs(angle) % (2 * pi)) # for angles multiple of 2*pi (numerically close), # the gates act as an identity (up to a global phase). if abs(angle) < self.eps: dag.remove_op_node(group[0]) else: if self.gate == 'rx': new_op = RXGate(angle) elif self.gate == 'rz': new_op = RZGate(angle) dag.substitute_node(group[0], new_op, inplace=True) for node in group[1:]: dag.remove_op_node(node) return dag
def makeCircuit6(graph, n, edges, cedges, gammasbetas, p): cgraph = nx.complement(graph) ans = ClassicalRegister(n) sol = QuantumRegister(n) QAOA = QuantumCircuit(sol, ans) r = 2 for j in range(p): for i in range(n): QAOA.u1(-gammasbetas[j], i) for l in range(r): for i in range(n): nbrs = 0 nbs = [] for nbr in cgraph[i]: QAOA.x(nbr) nbrs += 1 nbs.append(nbr) nbs.append(i) if (nbrs != 0): #gate = MCMT(RXGate(gammasbetas[p+j]/r),nbrs ,1) gate = MCMT(RXGate(2 * gammasbetas[l + j] / r), nbrs, 1) QAOA.append(gate, nbs) else: #QAOA.rx(gammasbetas[p+j]/r,i) QAOA.rx(2 * gammasbetas[l + j] / r, i) for nbr in cgraph[i]: QAOA.x(nbr) return QAOA
def makeCircuit9(graph, n, edges, cedges, gammasbetas, p, orderings): cgraph = nx.complement(graph) ans = ClassicalRegister(n) sol = QuantumRegister(n) QAOA = QuantumCircuit(sol, ans) for j in range(p): if (j != 0): for i in range(n): QAOA.u1(-gammasbetas[j - 1], i) QAOA.barrier() for i in orderings[j]: nbrs = 0 nbs = [] for nbr in cgraph[i]: QAOA.x(nbr) nbrs += 1 nbs.append(nbr) nbs.append(i) if (nbrs != 0): gate = MCMT(RXGate(2 * gammasbetas[p - 1 + j]), nbrs, 1) QAOA.append(gate, nbs) else: QAOA.rx(2 * gammasbetas[p - 1 + j], i) for nbr in cgraph[i]: QAOA.x(nbr) QAOA.barrier() return QAOA
def _circuit_zxz(theta, phi, lam, simplify=False, atol=DEFAULT_ATOL): if simplify and np.isclose(theta, 0.0, atol=atol): circuit = QuantumCircuit(1) circuit.append(RZGate(phi + lam), [0]) return circuit circuit = QuantumCircuit(1) if not simplify or not np.isclose(lam, 0.0, atol=atol): circuit.append(RZGate(lam), [0]) if not simplify or not np.isclose(theta, 0.0, atol=atol): circuit.append(RXGate(theta), [0]) if not simplify or not np.isclose(phi, 0.0, atol=atol): circuit.append(RZGate(phi), [0]) return circuit
def makeCircuit8(graph, n, edges, cedges, gammasbetas, p, orderings): cgraph = nx.complement(graph) ans = ClassicalRegister(n) sol = QuantumRegister(n) QAOA = QuantumCircuit(sol, ans) k = 1 #dickestate for i in range(n - 1, n - k - 1, -1): QAOA.x(i) for l in range(n, k, -1): scs(l, k, QAOA, n) for l in range(k, 1, -1): scs(l, l - 1, QAOA, n) for j in range(p): for i in range(n): QAOA.u1(-gammasbetas[j], i) for i in orderings[j]: nbrs = 0 nbs = [] for nbr in cgraph[i]: QAOA.x(nbr) nbrs += 1 nbs.append(nbr) nbs.append(i) if (nbrs != 0): gate = MCMT(RXGate(gammasbetas[p + j]), nbrs, 1) QAOA.append(gate, nbs) else: QAOA.rx(gammasbetas[p + j], i) for nbr in cgraph[i]: QAOA.x(nbr) return QAOA
def makeCircuit5(graph, n, edges, cedges, gammasbetas, p): cgraph = nx.complement(graph) ans = ClassicalRegister(n) sol = QuantumRegister(n) QAOA = QuantumCircuit(sol, ans) k = 1 #dickestate for i in range(n - 1, n - k - 1, -1): QAOA.x(i) for l in range(n, k, -1): scs(l, k, QAOA, n) for l in range(k, 1, -1): scs(l, l - 1, QAOA, n) #mixer and phaseseparation for j in range(p): for i in range(n): QAOA.u1(-gammasbetas[j], i) for i in range(n): nbrs = 0 nbs = [] for nbr in cgraph[i]: QAOA.x(nbr) nbrs += 1 nbs.append(nbr) nbs.append(i) if (nbrs != 0): gate = MCMT(RXGate(gammasbetas[p + j]), nbrs, 1) QAOA.append(gate, nbs) #else: #warum habe ich das ausgeklammert? Testen ob es besser ist? #QAOA.rx(gammasbetas[p+j],i) for nbr in cgraph[i]: QAOA.x(nbr) return QAOA
def fnx(circuit, qr): circuit.global_phase += np.pi / 4 circuit._append(RXGate(np.pi / 2), [qr[0]], [])
def _get_sx_vz_3cx_efficient_euler(self, decomposition, target_decomposed): """ Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT gates assuming three CNOT gates are needed. This first decomposes each unitary from the KAK decomposition into ZXZ on the source qubit of the CNOTs and XZX on the targets in order commute operators to beginning and end of decomposition. Inserting Hadamards reverses the direction of the CNOTs and transforms a variable Rx -> variable virtual Rz. The beginning and ending single qubit gates are then collapsed and re-decomposed with the single qubit decomposer. This last step could be avoided if performance is a concern. """ best_nbasis = 3 # by assumption num_1q_uni = len(decomposition) # create structure to hold euler angles: 1st index represents unitary "group" wrt cx # 2nd index represents index of euler triple. euler_q0 = np.empty((num_1q_uni // 2, 3), dtype=float) euler_q1 = np.empty((num_1q_uni // 2, 3), dtype=float) global_phase = 0.0 atol = 1e-10 # absolute tolerance for floats # decompose source unitaries to zxz zxz_decomposer = OneQubitEulerDecomposer("ZXZ") for iqubit, decomp in enumerate(decomposition[0::2]): euler_angles = zxz_decomposer.angles_and_phase(decomp) euler_q0[iqubit, [1, 2, 0]] = euler_angles[:3] global_phase += euler_angles[3] # decompose target unitaries to xzx xzx_decomposer = OneQubitEulerDecomposer("XZX") for iqubit, decomp in enumerate(decomposition[1::2]): euler_angles = xzx_decomposer.angles_and_phase(decomp) euler_q1[iqubit, [1, 2, 0]] = euler_angles[:3] global_phase += euler_angles[3] qc = QuantumCircuit(2) qc.global_phase = target_decomposed.global_phase qc.global_phase -= best_nbasis * self.basis.global_phase qc.global_phase += global_phase x12 = euler_q0[1][2] + euler_q0[2][0] x12_isNonZero = not math.isclose(x12, 0, abs_tol=atol) x12_isOddMult = None x12_isPiMult = math.isclose(math.sin(x12), 0, abs_tol=atol) if x12_isPiMult: x12_isOddMult = math.isclose(math.cos(x12), -1, abs_tol=atol) x12_phase = math.pi * math.cos(x12) x02_add = x12 - euler_q0[1][0] x12_isHalfPi = math.isclose(x12, math.pi / 2, abs_tol=atol) # TODO: make this more effecient to avoid double decomposition circ = QuantumCircuit(1) circ.rz(euler_q0[0][0], 0) circ.rx(euler_q0[0][1], 0) if x12_isNonZero and x12_isPiMult: circ.rz(euler_q0[0][2] - x02_add, 0) else: circ.rz(euler_q0[0][2] + euler_q0[1][0], 0) circ.h(0) qceuler = self._decomposer1q(Operator(circ).data) qc.compose(qceuler, [0], inplace=True) circ = QuantumCircuit(1) circ.rx(euler_q1[0][0], 0) circ.rz(euler_q1[0][1], 0) circ.rx(euler_q1[0][2] + euler_q1[1][0], 0) circ.h(0) qceuler = self._decomposer1q(Operator(circ).data) qc.compose(qceuler, [1], inplace=True) qc.cx(1, 0) if x12_isPiMult: # even or odd multiple if x12_isNonZero: qc.global_phase += x12_phase if x12_isNonZero and x12_isOddMult: qc.rz(-euler_q0[1][1], 0) else: qc.rz(euler_q0[1][1], 0) qc.global_phase += math.pi if x12_isHalfPi: qc.sx(0) qc.global_phase -= math.pi / 4 elif x12_isNonZero and not x12_isPiMult: # this is non-optimal but doesn't seem to occur currently if self.pulse_optimize is None: qc.compose(self._decomposer1q(Operator(RXGate(x12)).data), [0], inplace=True) else: raise QiskitError( "possible non-pulse-optimal decomposition encountered") if math.isclose(euler_q1[1][1], math.pi / 2, abs_tol=atol): qc.sx(1) qc.global_phase -= math.pi / 4 else: # this is non-optimal but doesn't seem to occur currently if self.pulse_optimize is None: qc.compose(self._decomposer1q( Operator(RXGate(euler_q1[1][1])).data), [1], inplace=True) else: raise QiskitError( "possible non-pulse-optimal decomposition encountered") qc.rz(euler_q1[1][2] + euler_q1[2][0], 1) qc.cx(1, 0) qc.rz(euler_q0[2][1], 0) if math.isclose(euler_q1[2][1], math.pi / 2, abs_tol=atol): qc.sx(1) qc.global_phase -= math.pi / 4 else: # this is non-optimal but doesn't seem to occur currently if self.pulse_optimize is None: qc.compose(self._decomposer1q( Operator(RXGate(euler_q1[2][1])).data), [1], inplace=True) else: raise QiskitError( "possible non-pulse-optimal decomposition encountered") qc.cx(1, 0) circ = QuantumCircuit(1) circ.h(0) circ.rz(euler_q0[2][2] + euler_q0[3][0], 0) circ.rx(euler_q0[3][1], 0) circ.rz(euler_q0[3][2], 0) qceuler = self._decomposer1q(Operator(circ).data) qc.compose(qceuler, [0], inplace=True) circ = QuantumCircuit(1) circ.h(0) circ.rx(euler_q1[2][2] + euler_q1[3][0], 0) circ.rz(euler_q1[3][1], 0) circ.rx(euler_q1[3][2], 0) qceuler = self._decomposer1q(Operator(circ).data) qc.compose(qceuler, [1], inplace=True) # TODO: fix the sign problem to avoid correction here if cmath.isclose(target_decomposed.unitary_matrix[0, 0], -(Operator(qc).data[0, 0]), abs_tol=atol): qc.global_phase += math.pi return qc
def _hessian_states( self, state_op: StateFn, meas_op: Optional[OperatorBase] = None, target_params: Optional[Union[Tuple[ParameterExpression, ParameterExpression], List[Tuple[ParameterExpression, ParameterExpression]]]] = None ) -> OperatorBase: """Generate the operator states whose evaluation returns the Hessian (items). Args: state_op: The operator representing the quantum state for which we compute the Hessian. meas_op: The operator representing the observable for which we compute the gradient. target_params: The parameters we are computing the Hessian wrt: ω Returns: Operators which give the Hessian. If a parameter appears multiple times, one circuit is created per parameterized gates to compute the product rule. Raises: AquaError: If one of the circuits could not be constructed. TypeError: If ``operator`` is of unsupported type. """ state_qc = deepcopy(state_op.primitive) if isinstance(target_params, list) and isinstance( target_params[0], tuple): tuples_list = deepcopy(target_params) target_params = [] for tuples in tuples_list: if all([ param in state_qc._parameter_table.get_keys() for param in tuples ]): for param in tuples: if param not in target_params: target_params.append(param) elif isinstance(target_params, tuple): tuples_list = deepcopy([target_params]) target_params = [] for tuples in tuples_list: if all([ param in state_qc._parameter_table.get_keys() for param in tuples ]): for param in tuples: if param not in target_params: target_params.append(param) else: raise TypeError( 'Please define in the parameters for which the Hessian is evaluated either ' 'as parameter tuple or a list of parameter tuples') qr_add0 = QuantumRegister(1, 'work_qubit0') work_q0 = qr_add0[0] qr_add1 = QuantumRegister(1, 'work_qubit1') work_q1 = qr_add1[0] # create a copy of the original circuit with an additional working qubit register circuit = state_qc.copy() circuit.add_register(qr_add0, qr_add1) # Get the circuits needed to compute the Hessian hessian_ops = None for param_a, param_b in tuples_list: if param_a not in state_qc._parameter_table.get_keys() or param_b \ not in state_qc._parameter_table.get_keys(): hessian_op = ~Zero @ One else: param_gates_a = state_qc._parameter_table[param_a] param_gates_b = state_qc._parameter_table[param_b] for i, param_occurence_a in enumerate(param_gates_a): coeffs_a, gates_a = self._gate_gradient_dict( param_occurence_a[0])[param_occurence_a[1]] # apply Hadamard on working qubit self.insert_gate(circuit, param_occurence_a[0], HGate(), qubits=[work_q0]) self.insert_gate(circuit, param_occurence_a[0], HGate(), qubits=[work_q1]) for j, gate_to_insert_a in enumerate(gates_a): coeff_a = coeffs_a[j] hessian_circuit_temp = QuantumCircuit(*circuit.qregs) hessian_circuit_temp.data = circuit.data # Fix working qubit 0 phase sign = np.sign(coeff_a) is_complex = np.iscomplex(coeff_a) if sign == -1: if is_complex: self.insert_gate(hessian_circuit_temp, param_occurence_a[0], SdgGate(), qubits=[work_q0]) else: self.insert_gate(hessian_circuit_temp, param_occurence_a[0], ZGate(), qubits=[work_q0]) else: if is_complex: self.insert_gate(hessian_circuit_temp, param_occurence_a[0], SGate(), qubits=[work_q0]) # Insert controlled, intercepting gate - controlled by |1> if isinstance(param_occurence_a[0], UGate): if param_occurence_a[1] == 0: self.insert_gate( hessian_circuit_temp, param_occurence_a[0], RZGate(param_occurence_a[0].params[2])) self.insert_gate(hessian_circuit_temp, param_occurence_a[0], RXGate(np.pi / 2)) self.insert_gate(hessian_circuit_temp, param_occurence_a[0], gate_to_insert_a, additional_qubits=([work_q0], [])) self.insert_gate(hessian_circuit_temp, param_occurence_a[0], RXGate(-np.pi / 2)) self.insert_gate( hessian_circuit_temp, param_occurence_a[0], RZGate(-param_occurence_a[0].params[2])) elif param_occurence_a[1] == 1: self.insert_gate(hessian_circuit_temp, param_occurence_a[0], gate_to_insert_a, after=True, additional_qubits=([work_q0], [])) else: self.insert_gate(hessian_circuit_temp, param_occurence_a[0], gate_to_insert_a, additional_qubits=([work_q0], [])) else: self.insert_gate(hessian_circuit_temp, param_occurence_a[0], gate_to_insert_a, additional_qubits=([work_q0], [])) for m, param_occurence_b in enumerate(param_gates_b): coeffs_b, gates_b = self._gate_gradient_dict( param_occurence_b[0])[param_occurence_b[1]] for n, gate_to_insert_b in enumerate(gates_b): coeff_b = coeffs_b[n] # create a copy of the original circuit with the same registers hessian_circuit = QuantumCircuit( *hessian_circuit_temp.qregs) hessian_circuit.data = hessian_circuit_temp.data # Fix working qubit 1 phase sign = np.sign(coeff_b) is_complex = np.iscomplex(coeff_b) if sign == -1: if is_complex: self.insert_gate(hessian_circuit, param_occurence_b[0], SdgGate(), qubits=[work_q1]) else: self.insert_gate(hessian_circuit, param_occurence_b[0], ZGate(), qubits=[work_q1]) else: if is_complex: self.insert_gate(hessian_circuit, param_occurence_b[0], SGate(), qubits=[work_q1]) # Insert controlled, intercepting gate - controlled by |1> if isinstance(param_occurence_b[0], UGate): if param_occurence_b[1] == 0: self.insert_gate( hessian_circuit, param_occurence_b[0], RZGate(param_occurence_b[0]. params[2])) self.insert_gate( hessian_circuit, param_occurence_b[0], RXGate(np.pi / 2)) self.insert_gate( hessian_circuit, param_occurence_b[0], gate_to_insert_b, additional_qubits=([work_q1], [])) self.insert_gate( hessian_circuit, param_occurence_b[0], RXGate(-np.pi / 2)) self.insert_gate( hessian_circuit, param_occurence_b[0], RZGate(-param_occurence_b[0]. params[2])) elif param_occurence_b[1] == 1: self.insert_gate( hessian_circuit, param_occurence_b[0], gate_to_insert_b, after=True, additional_qubits=([work_q1], [])) else: self.insert_gate( hessian_circuit, param_occurence_b[0], gate_to_insert_b, additional_qubits=([work_q1], [])) else: self.insert_gate( hessian_circuit, param_occurence_b[0], gate_to_insert_b, additional_qubits=([work_q1], [])) hessian_circuit.h(work_q0) hessian_circuit.cz(work_q1, work_q0) hessian_circuit.h(work_q1) term = state_op.coeff * np.sqrt(np.abs(coeff_a) * np.abs(coeff_b)) \ * CircuitStateFn(hessian_circuit) # Chain Rule Parameter Expression gate_param_a = param_occurence_a[0].params[ param_occurence_a[1]] gate_param_b = param_occurence_b[0].params[ param_occurence_b[1]] if meas_op: meas = deepcopy(meas_op) if isinstance(gate_param_a, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param_a, param_a) meas *= expr_grad if isinstance(gate_param_b, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param_a, param_a) meas *= expr_grad term = meas @ term else: term = ListOp([term], combo_fn=partial( self._hess_combo_fn, state_op=state_op)) if isinstance(gate_param_a, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param_a, param_a) term *= expr_grad if isinstance(gate_param_b, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param_a, param_a) term *= expr_grad if i == 0 and j == 0 and m == 0 and n == 0: hessian_op = term else: # Product Rule hessian_op += term # Create a list of Hessian elements w.r.t. the given parameter tuples if len(tuples_list) == 1: return hessian_op else: if not hessian_ops: hessian_ops = [hessian_op] else: hessian_ops += [hessian_op] return ListOp(hessian_ops)
def _gradient_states( self, state_op: StateFn, meas_op: Optional[OperatorBase] = None, target_params: Optional[Union[ParameterExpression, ParameterVector, List[ParameterExpression]]] = None ) -> ListOp: """Generate the gradient states. Args: state_op: The operator representing the quantum state for which we compute the gradient. meas_op: The operator representing the observable for which we compute the gradient. target_params: The parameters we are taking the gradient wrt: ω Returns: ListOp of StateFns as quantum circuits which are the states w.r.t. which we compute the gradient. If a parameter appears multiple times, one circuit is created per parameterized gates to compute the product rule. Raises: AquaError: If one of the circuits could not be constructed. TypeError: If the operators is of unsupported type. """ state_qc = deepcopy(state_op.primitive) # Define the working qubit to realize the linear combination of unitaries qr_work = QuantumRegister(1, 'work_qubit_lin_comb_grad') work_q = qr_work[0] if not isinstance(target_params, (list, np.ndarray)): target_params = [target_params] if len(target_params) > 1: states = None additional_qubits: Tuple[List[Qubit], List[Qubit]] = ([work_q], []) for param in target_params: if param not in state_qc._parameter_table.get_keys(): op = ~Zero @ One else: param_gates = state_qc._parameter_table[param] for m, param_occurence in enumerate(param_gates): coeffs, gates = self._gate_gradient_dict( param_occurence[0])[param_occurence[1]] # construct the states for k, gate_to_insert in enumerate(gates): grad_state = QuantumCircuit(*state_qc.qregs, qr_work) grad_state.compose(state_qc, inplace=True) # apply Hadamard on work_q self.insert_gate(grad_state, param_occurence[0], HGate(), qubits=[work_q]) # Fix work_q phase coeff_i = coeffs[k] sign = np.sign(coeff_i) is_complex = np.iscomplex(coeff_i) if sign == -1: if is_complex: self.insert_gate(grad_state, param_occurence[0], SdgGate(), qubits=[work_q]) else: self.insert_gate(grad_state, param_occurence[0], ZGate(), qubits=[work_q]) else: if is_complex: self.insert_gate(grad_state, param_occurence[0], SGate(), qubits=[work_q]) # Insert controlled, intercepting gate - controlled by |0> if isinstance(param_occurence[0], UGate): if param_occurence[1] == 0: self.insert_gate( grad_state, param_occurence[0], RZGate(param_occurence[0].params[2])) self.insert_gate(grad_state, param_occurence[0], RXGate(np.pi / 2)) self.insert_gate( grad_state, param_occurence[0], gate_to_insert, additional_qubits=additional_qubits) self.insert_gate(grad_state, param_occurence[0], RXGate(-np.pi / 2)) self.insert_gate( grad_state, param_occurence[0], RZGate(-param_occurence[0].params[2])) elif param_occurence[1] == 1: self.insert_gate( grad_state, param_occurence[0], gate_to_insert, after=True, additional_qubits=additional_qubits) else: self.insert_gate( grad_state, param_occurence[0], gate_to_insert, additional_qubits=additional_qubits) else: self.insert_gate( grad_state, param_occurence[0], gate_to_insert, additional_qubits=additional_qubits) grad_state.h(work_q) state = np.sqrt( np.abs(coeff_i)) * state_op.coeff * CircuitStateFn( grad_state) # Chain Rule parameter expressions gate_param = param_occurence[0].params[ param_occurence[1]] if meas_op: if gate_param == param: state = meas_op @ state else: if isinstance(gate_param, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param, param) state = (expr_grad * meas_op) @ state else: state = ~Zero @ One else: if gate_param == param: state = ListOp([state], combo_fn=partial( self._grad_combo_fn, state_op=state_op)) else: if isinstance(gate_param, ParameterExpression): expr_grad = DerivativeBase.parameter_expression_grad( gate_param, param) state = expr_grad * ListOp( [state], combo_fn=partial(self._grad_combo_fn, state_op=state_op)) else: state = ~Zero @ One if m == 0 and k == 0: op = state else: # Product Rule op += state if len(target_params) > 1: if not states: states = [op] else: states += [op] else: return op if len(target_params) > 1: return ListOp(states) else: return op
from qiskit.circuit import Parameter from qiskit import QuantumCircuit, QuantumRegister from basis_library import BasisLibrary from qiskit.circuit.library.standard_gates import (HGate, ZGate, XGate, YGate, RZGate, RXGate, RYGate, CXGate, CZGate) libr = RxzCzLibrary = BasisLibrary() # H - Gate q = QuantumRegister(1, 'q') _def = QuantumCircuit(q, global_phase=pi / 2) rules = [(RZGate(pi / 2), [q[0]], []), (RXGate(pi / 2), [q[0]], []), (RZGate(pi / 2), [q[0]], [])] for inst, qargs, cargs in rules: _def.append(inst, qargs, cargs) libr.add('h', _def) # Z - Gate q = QuantumRegister(1, 'q') _def = QuantumCircuit(q, global_phase=pi / 2) rules = [(RZGate(pi), [q[0]], [])] for inst, qargs, cargs in rules: _def.append(inst, qargs, cargs)
def apply_grad_gate(circuit, gate, param_index, grad_gate, grad_coeff, qr_superpos, open_ctrl=False, trim_after_grad_gate=False): """Util function to apply a gradient gate for the linear combination of unitaries method. Replaces the ``gate`` instance in ``circuit`` with ``grad_gate`` using ``qr_superpos`` as superposition qubit. Also adds the appropriate sign-fix gates on the superposition qubit. Args: circuit (QuantumCircuit): The circuit in which to do the replacements. gate (Gate): The gate instance to replace. param_index (int): The index of the parameter in ``gate``. grad_gate (Gate): A controlled gate encoding the gradient of ``gate``. grad_coeff (float): A coefficient to the gradient component. Might not be one if the gradient contains multiple summed terms. qr_superpos (QuantumRegister): A ``QuantumRegister`` of size 1 contained in ``circuit`` that is used as control for ``grad_gate``. open_ctrl (bool): If True use an open control for ``grad_gate`` instead of closed. trim_after_grad_gate (bool): If True remove all gates after the ``grad_gate``. Can be used to reduce the circuit depth in e.g. computing an overlap of gradients. Returns: QuantumCircuit: A copy of the original circuit with the gradient gate added. Raises: RuntimeError: If ``gate`` is not in ``circuit``. """ # copy the input circuit taking the gates by reference out = QuantumCircuit(*circuit.qregs) out._data = circuit._data.copy() out._parameter_table = ParameterTable({ param: values.copy() for param, values in circuit._parameter_table.items() }) # get the data index and qubits of the target gate TODO use built-in gate_idx, gate_qubits = None, None for i, (op, qarg, _) in enumerate(out._data): if op is gate: gate_idx, gate_qubits = i, qarg break if gate_idx is None: raise RuntimeError('The specified gate could not be found in the circuit data.') # initialize replacement instructions replacement = [] # insert the phase fix before the target gate better documentation sign = np.sign(grad_coeff) is_complex = np.iscomplex(grad_coeff) if sign < 0 and is_complex: replacement.append((SdgGate(), qr_superpos[:], [])) elif sign < 0: replacement.append((ZGate(), qr_superpos[:], [])) elif is_complex: replacement.append((SGate(), qr_superpos[:], [])) # else no additional gate required # open control if specified if open_ctrl: replacement += [(XGate(), qr_superpos[:], [])] # compute the replacement if isinstance(gate, UGate) and param_index == 0: theta = gate.params[2] rz_plus, rz_minus = RZGate(theta), RZGate(-theta) replacement += [(rz_plus, [qubit], []) for qubit in gate_qubits] replacement += [(RXGate(np.pi / 2), [qubit], []) for qubit in gate_qubits] replacement.append((grad_gate, qr_superpos[:] + gate_qubits, [])) replacement += [(RXGate(-np.pi / 2), [qubit], []) for qubit in gate_qubits] replacement += [(rz_minus, [qubit], []) for qubit in gate_qubits] # update parametertable if necessary if isinstance(theta, ParameterExpression): out._update_parameter_table(rz_plus) out._update_parameter_table(rz_minus) if open_ctrl: replacement += [(XGate(), qr_superpos[:], [])] if not trim_after_grad_gate: replacement.append((gate, gate_qubits, [])) elif isinstance(gate, UGate) and param_index == 1: # gradient gate is applied after the original gate in this case replacement.append((gate, gate_qubits, [])) replacement.append((grad_gate, qr_superpos[:] + gate_qubits, [])) if open_ctrl: replacement += [(XGate(), qr_superpos[:], [])] else: replacement.append((grad_gate, qr_superpos[:] + gate_qubits, [])) if open_ctrl: replacement += [(XGate(), qr_superpos[:], [])] if not trim_after_grad_gate: replacement.append((gate, gate_qubits, [])) # replace the parameter we compute the derivative of with the replacement # TODO can this be done more efficiently? if trim_after_grad_gate: # remove everything after the gradient gate out._data[gate_idx:] = replacement # reset parameter table table = ParameterTable() for op, _, _ in out._data: for idx, param_expression in enumerate(op.params): if isinstance(param_expression, ParameterExpression): for param in param_expression.parameters: if param not in table.keys(): table[param] = [(op, idx)] else: table[param].append((op, idx)) out._parameter_table = table else: out._data[gate_idx:gate_idx + 1] = replacement return out
def apply_grad_gate( circuit, gate, param_index, grad_gate, grad_coeff, qr_superpos, open_ctrl=False, trim_after_grad_gate=False, ): """Util function to apply a gradient gate for the linear combination of unitaries method. Replaces the ``gate`` instance in ``circuit`` with ``grad_gate`` using ``qr_superpos`` as superposition qubit. Also adds the appropriate sign-fix gates on the superposition qubit. Args: circuit (QuantumCircuit): The circuit in which to do the replacements. gate (Gate): The gate instance to replace. param_index (int): The index of the parameter in ``gate``. grad_gate (Gate): A controlled gate encoding the gradient of ``gate``. grad_coeff (float): A coefficient to the gradient component. Might not be one if the gradient contains multiple summed terms. qr_superpos (QuantumRegister): A ``QuantumRegister`` of size 1 contained in ``circuit`` that is used as control for ``grad_gate``. open_ctrl (bool): If True use an open control for ``grad_gate`` instead of closed. trim_after_grad_gate (bool): If True remove all gates after the ``grad_gate``. Can be used to reduce the circuit depth in e.g. computing an overlap of gradients. Returns: QuantumCircuit: A copy of the original circuit with the gradient gate added. Raises: RuntimeError: If ``gate`` is not in ``circuit``. """ qr_superpos_qubits = tuple(qr_superpos) # copy the input circuit taking the gates by reference out = QuantumCircuit(*circuit.qregs) out._data = circuit._data.copy() out._parameter_table = ParameterTable({ param: values.copy() for param, values in circuit._parameter_table.items() }) # get the data index and qubits of the target gate TODO use built-in gate_idx, gate_qubits = None, None for i, instruction in enumerate(out._data): if instruction.operation is gate: gate_idx, gate_qubits = i, instruction.qubits break if gate_idx is None: raise RuntimeError( "The specified gate could not be found in the circuit data.") # initialize replacement instructions replacement = [] # insert the phase fix before the target gate better documentation sign = np.sign(grad_coeff) is_complex = np.iscomplex(grad_coeff) if sign < 0 and is_complex: replacement.append( CircuitInstruction(SdgGate(), qr_superpos_qubits, ())) elif sign < 0: replacement.append( CircuitInstruction(ZGate(), qr_superpos_qubits, ())) elif is_complex: replacement.append( CircuitInstruction(SGate(), qr_superpos_qubits, ())) # else no additional gate required # open control if specified if open_ctrl: replacement += [ CircuitInstruction(XGate(), qr_superpos_qubits, []) ] # compute the replacement if isinstance(gate, UGate) and param_index == 0: theta = gate.params[2] rz_plus, rz_minus = RZGate(theta), RZGate(-theta) replacement += [ CircuitInstruction(rz_plus, (qubit, ), ()) for qubit in gate_qubits ] replacement += [ CircuitInstruction(RXGate(np.pi / 2), (qubit, ), ()) for qubit in gate_qubits ] replacement.append( CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits, [])) replacement += [ CircuitInstruction(RXGate(-np.pi / 2), (qubit, ), ()) for qubit in gate_qubits ] replacement += [ CircuitInstruction(rz_minus, (qubit, ), ()) for qubit in gate_qubits ] # update parametertable if necessary if isinstance(theta, ParameterExpression): # This dangerously subverts ParameterTable by abusing the fact that binding will # mutate the exact instruction instance, and relies on all instances of `rz_plus` # that were added before being the same in memory, which QuantumCircuit usually # ensures is not the case. I'm leaving this as close to its previous form as # possible, to avoid introducing further complications, but this whole method # accesses internal attributes of `QuantumCircuit` and needs rewriting. # - Jake Lishman, 2022-03-02. out._update_parameter_table( CircuitInstruction(rz_plus, (gate_qubits[0], ), ())) out._update_parameter_table( CircuitInstruction(rz_minus, (gate_qubits[0], ), ())) if open_ctrl: replacement.append( CircuitInstruction(XGate(), qr_superpos_qubits, ())) if not trim_after_grad_gate: replacement.append(CircuitInstruction(gate, gate_qubits, ())) elif isinstance(gate, UGate) and param_index == 1: # gradient gate is applied after the original gate in this case replacement.append(CircuitInstruction(gate, gate_qubits, ())) replacement.append( CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits, ())) if open_ctrl: replacement.append( CircuitInstruction(XGate(), qr_superpos_qubits, ())) else: replacement.append( CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits, ())) if open_ctrl: replacement.append( CircuitInstruction(XGate(), qr_superpos_qubits, ())) if not trim_after_grad_gate: replacement.append(CircuitInstruction(gate, gate_qubits, ())) # replace the parameter we compute the derivative of with the replacement # TODO can this be done more efficiently? if trim_after_grad_gate: # remove everything after the gradient gate out._data[gate_idx:] = replacement # reset parameter table table = ParameterTable() for instruction in out._data: for idx, param_expression in enumerate( instruction.operation.params): if isinstance(param_expression, ParameterExpression): for param in param_expression.parameters: if param not in table.keys(): table[param] = ParameterReferences( ((instruction.operation, idx), )) else: table[param].add((instruction.operation, idx)) out._parameter_table = table else: out._data[gate_idx:gate_idx + 1] = replacement return out