def test_qregs(self): """Test getting quantum registers from circuit.""" qr1 = QuantumRegister(10, "q") self.assertEqual(qr1.name, "q") self.assertEqual(qr1.size, 10) self.assertEqual(type(qr1), QuantumRegister)
def test_append_opaque_wrong_dimension(self): """test appending opaque gate to wrong dimension wires.""" qr = QuantumRegister(2) circ = QuantumCircuit(qr) opaque_gate = Gate(name="crz_2", num_qubits=2, params=[0.5]) self.assertRaises(CircuitError, circ.append, opaque_gate, [qr[0]])
def run(self, dag): """Run the ConsolidateBlocks pass on `dag`. Iterate over each block and replace it with an equivalent Unitary on the same wires. """ if self.decomposer is None: return dag new_dag = DAGCircuit() for qreg in dag.qregs.values(): new_dag.add_qreg(qreg) for creg in dag.cregs.values(): new_dag.add_creg(creg) # compute ordered indices for the global circuit wires global_index_map = {wire: idx for idx, wire in enumerate(dag.qubits)} blocks = self.property_set['block_list'] # just to make checking if a node is in any block easier all_block_nodes = {nd for bl in blocks for nd in bl} for node in dag.topological_op_nodes(): if node not in all_block_nodes: # need to add this node to find out where in the list it goes preds = [ nd for nd in dag.predecessors(node) if nd.type == 'op' ] block_count = 0 while preds: if block_count < len(blocks): block = blocks[block_count] # if any of the predecessors are in the block, remove them preds = [p for p in preds if p not in block] else: # should never occur as this would mean not all # nodes before this one topologically had been added # so not all predecessors were removed raise TranspilerError( "Not all predecessors removed due to error" " in topological order") block_count += 1 # we have now seen all predecessors # so update the blocks list to include this block blocks = blocks[:block_count] + [[node]] + blocks[block_count:] # create the dag from the updated list of blocks basis_gate_name = self.decomposer.gate.name for block in blocks: if len(block) == 1 and (block[0].name != basis_gate_name or block[0].op.is_parameterized()): # an intermediate node that was added into the overall list new_dag.apply_operation_back(block[0].op, block[0].qargs, block[0].cargs) else: # find the qubits involved in this block block_qargs = set() block_cargs = set() for nd in block: block_qargs |= set(nd.qargs) if nd.condition: block_cargs |= set(nd.condition[0]) # convert block to a sub-circuit, then simulate unitary and add q = QuantumRegister(len(block_qargs)) # if condition in node, add clbits to circuit if len(block_cargs) > 0: c = ClassicalRegister(len(block_cargs)) subcirc = QuantumCircuit(q, c) else: subcirc = QuantumCircuit(q) block_index_map = self._block_qargs_to_indices( block_qargs, global_index_map) basis_count = 0 for nd in block: if nd.op.name == basis_gate_name: basis_count += 1 subcirc.append(nd.op, [q[block_index_map[i]] for i in nd.qargs]) unitary = UnitaryGate( Operator(subcirc)) # simulates the circuit max_2q_depth = 20 # If depth > 20, there will be 1q gates to consolidate. if (self.force_consolidate or unitary.num_qubits > 2 or self.decomposer.num_basis_gates(unitary) < basis_count or len(subcirc) > max_2q_depth): new_dag.apply_operation_back( UnitaryGate(unitary), sorted(block_qargs, key=lambda x: block_index_map[x])) else: for nd in block: new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs) return new_dag
CCXGate, YGate, CYGate, RYYGate, ECRGate, ZGate, CZGate, ) _sel = StandardEquivalenceLibrary = EquivalenceLibrary() # Import existing gate definitions # HGate q = QuantumRegister(1, 'q') def_h = QuantumCircuit(q) def_h.append(U2Gate(0, pi), [q[0]], []) _sel.add_equivalence(HGate(), def_h) # CHGate q = QuantumRegister(2, 'q') def_ch = QuantumCircuit(q) for inst, qargs, cargs in [(SGate(), [q[1]], []), (HGate(), [q[1]], []), (TGate(), [q[1]], []), (CXGate(), [q[0], q[1]], []), (TdgGate(), [q[1]], []), (HGate(), [q[1]], []), (SdgGate(), [q[1]], [])]: def_ch.append(inst, qargs, cargs) _sel.add_equivalence(CHGate(), def_ch)
def test_dag_get_qubits(self): """get_qubits() method """ dag = DAGCircuit() dag.add_qreg(QuantumRegister(1, 'qr1')) dag.add_qreg(QuantumRegister(1, 'qr10')) dag.add_qreg(QuantumRegister(1, 'qr0')) dag.add_qreg(QuantumRegister(1, 'qr3')) dag.add_qreg(QuantumRegister(1, 'qr4')) dag.add_qreg(QuantumRegister(1, 'qr6')) self.assertListEqual(dag.qubits(), [(QuantumRegister(1, 'qr1'), 0), (QuantumRegister(1, 'qr10'), 0), (QuantumRegister(1, 'qr0'), 0), (QuantumRegister(1, 'qr3'), 0), (QuantumRegister(1, 'qr4'), 0), (QuantumRegister(1, 'qr6'), 0)])
import os from qiskit import execute from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit from quantuminspire.credentials import get_authentication from quantuminspire.qiskit import QI QI_URL = os.getenv('API_URL', 'https://api.quantum-inspire.com/') project_name = 'Qiskit-entangle' authentication = get_authentication() QI.set_authentication(authentication, QI_URL, project_name=project_name) qi_backend = QI.get_backend('QX single-node simulator') q = QuantumRegister(2) b = ClassicalRegister(2) circuit = QuantumCircuit(q, b) circuit.h(q[0]) circuit.cx(q[0], q[1]) circuit.measure(q, b) qi_job = execute(circuit, backend=qi_backend, shots=256) qi_result = qi_job.result() histogram = qi_result.get_counts(circuit) print('\nState\tCounts') [ print('{0}\t\t{1}'.format(state, counts)) for state, counts in histogram.items() ]
def __init__(self, num_result_qubits: Optional[int] = None, quadratic: Optional[Union[np.ndarray, List[List[Union[ float, ParameterExpression]]]]] = None, linear: Optional[Union[np.ndarray, List[Union[ float, ParameterExpression]]]] = None, offset: Optional[Union[float, ParameterExpression]] = None, little_endian: bool = True) -> None: r""" Args: num_result_qubits: The number of qubits to encode the result. Called :math:`m` in the class documentation. quadratic: A matrix containing the quadratic coefficients, :math:`A`. linear: An array containing the linear coefficients, :math:`b`. offset: A constant offset, :math:`c`. little_endian: Encode the result in little endianness. Raises: ValueError: If ``linear`` and ``quadratic`` have mismatching sizes. ValueError: If ``num_result_qubits`` is unspecified but cannot be determined because some values of the quadratic form are parameterized. """ # check inputs match if quadratic is not None and linear is not None: if len(quadratic) != len(linear): raise ValueError('Mismatching sizes of quadratic and linear.') # temporarily set quadratic and linear to [] instead of None so we can iterate over them if quadratic is None: quadratic = [] if linear is None: linear = [] if offset is None: offset = 0 num_input_qubits = np.max([1, len(linear), len(quadratic)]) # deduce number of result bits if not added if num_result_qubits is None: # check no value is parameterized if (any( any(isinstance(q_ij, ParameterExpression) for q_ij in q_i) for q_i in quadratic) or any( isinstance(l_i, ParameterExpression) for l_i in linear) or isinstance(offset, ParameterExpression)): raise ValueError( 'If the number of result qubits is not specified, the quadratic ' 'form matrices/vectors/offset may not be parameterized.') num_result_qubits = self.required_result_qubits( quadratic, linear, offset) qr_input = QuantumRegister(num_input_qubits) qr_result = QuantumRegister(num_result_qubits) super().__init__(qr_input, qr_result, name='Q(x)') # set quadratic and linear again to None if they were None if len(quadratic) == 0: quadratic = None if len(linear) == 0: linear = None scaling = np.pi * 2**(1 - num_result_qubits) # initial QFT (just hadamards) self.h(qr_result) if little_endian: qr_result = qr_result[::-1] # constant coefficient if offset != 0: for i, q_i in enumerate(qr_result): self.u1(scaling * 2**i * offset, q_i) # the linear part consists of the vector and the diagonal of the # matrix, since x_i * x_i = x_i, as x_i is a binary variable for j in range(num_input_qubits): value = linear[j] if linear is not None else 0 value += quadratic[j][j] if quadratic is not None else 0 if value != 0: for i, q_i in enumerate(qr_result): self.cu1(scaling * 2**i * value, qr_input[j], q_i) # the quadratic part adds A_ij and A_ji as x_i x_j == x_j x_i if quadratic is not None: for j in range(num_input_qubits): for k in range(j + 1, num_input_qubits): value = quadratic[j][k] + quadratic[k][j] if value != 0: for i, q_i in enumerate(qr_result): self.mcu1(scaling * 2**i * value, [qr_input[j], qr_input[k]], q_i) # add the inverse QFT iqft = QFT(num_result_qubits, do_swaps=False).inverse() self.append(iqft, qr_result)
def _define(self): """The standard definition used the Gray code implementation.""" q = QuantumRegister(self.num_qubits, name='q') self.definition = [(MCXGrayCode(self.num_ctrl_qubits), q[:], [])]
def _define(self): """Define the MCX gate using a V-chain of CX gates.""" q = QuantumRegister(self.num_qubits, name='q') q_controls = q[:self.num_ctrl_qubits] q_target = q[self.num_ctrl_qubits] q_ancillas = q[self.num_ctrl_qubits + 1:] definition = [] if self._dirty_ancillas: i = self.num_ctrl_qubits - 3 ancilla_pre_rule = [ (U2Gate(0, numpy.pi), [q_target], []), (CXGate(), [q_target, q_ancillas[i]], []), (U1Gate(-numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_controls[-1], q_ancillas[i]], []), (U1Gate(numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_target, q_ancillas[i]], []), (U1Gate(-numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_controls[-1], q_ancillas[i]], []), (U1Gate(numpy.pi / 4), [q_ancillas[i]], []), ] for inst in ancilla_pre_rule: definition.append(inst) for j in reversed(range(2, self.num_ctrl_qubits - 1)): definition.append( (RCCXGate(), [q_controls[j], q_ancillas[i - 1], q_ancillas[i]], [])) i -= 1 definition.append( (RCCXGate(), [q_controls[0], q_controls[1], q_ancillas[0]], [])) i = 0 for j in range(2, self.num_ctrl_qubits - 1): definition.append( (RCCXGate(), [q_controls[j], q_ancillas[i], q_ancillas[i + 1]], [])) i += 1 if self._dirty_ancillas: ancilla_post_rule = [ (U1Gate(-numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_controls[-1], q_ancillas[i]], []), (U1Gate(numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_target, q_ancillas[i]], []), (U1Gate(-numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_controls[-1], q_ancillas[i]], []), (U1Gate(numpy.pi / 4), [q_ancillas[i]], []), (CXGate(), [q_target, q_ancillas[i]], []), (U2Gate(0, numpy.pi), [q_target], []), ] for inst in ancilla_post_rule: definition.append(inst) else: definition.append( (CCXGate(), [q_controls[-1], q_ancillas[i], q_target], [])) for j in reversed(range(2, self.num_ctrl_qubits - 1)): definition.append( (RCCXGate(), [q_controls[j], q_ancillas[i - 1], q_ancillas[i]], [])) i -= 1 definition.append( (RCCXGate(), [q_controls[0], q_controls[1], q_ancillas[i]], [])) if self._dirty_ancillas: for i, j in enumerate(list(range(2, self.num_ctrl_qubits - 1))): definition.append( (RCCXGate(), [q_controls[j], q_ancillas[i], q_ancillas[i + 1]], [])) self.definition = definition
def test_qarg_noninteger_float(self): """Test attempt to pass non-integer float to QuantumRegister.""" self.assertRaises(CircuitError, QuantumRegister, 2.2) # but an integer float should pass qr = QuantumRegister(2.0) self.assertEqual(qr.size, 2)
def _multiplex(self, target_gate, list_of_angles): """ Return a recursive implementation of a multiplexor circuit, where each instruction itself has a decomposition based on smaller multiplexors. The LSB is the multiplexor "data" and the other bits are multiplexor "select". Args: target_gate (Gate): Ry or Rz gate to apply to target qubit, multiplexed over all other "select" qubits list_of_angles (list[float]): list of rotation angles to apply Ry and Rz Returns: DAGCircuit: the circuit implementing the multiplexor's action """ list_len = len(list_of_angles) local_num_qubits = int(math.log2(list_len)) + 1 q = QuantumRegister(local_num_qubits) circuit = QuantumCircuit(q, name="multiplex" + local_num_qubits.__str__()) lsb = q[0] msb = q[local_num_qubits - 1] # case of no multiplexing: base case for recursion if local_num_qubits == 1: circuit.append(target_gate(list_of_angles[0]), [q[0]]) return circuit # calc angle weights, assuming recursion (that is the lower-level # requested angles have been correctly implemented by recursion angle_weight = scipy.kron([[0.5, 0.5], [0.5, -0.5]], np.identity(2**(local_num_qubits - 2))) # calc the combo angles list_of_angles = angle_weight.dot(np.array(list_of_angles)).tolist() # recursive step on half the angles fulfilling the above assumption multiplex_1 = self._multiplex(target_gate, list_of_angles[0:(list_len // 2)]) circuit.append(multiplex_1.to_instruction(), q[0:-1]) # attach CNOT as follows, thereby flipping the LSB qubit circuit.append(CnotGate(), [msb, lsb]) # implement extra efficiency from the paper of cancelling adjacent # CNOTs (by leaving out last CNOT and reversing (NOT inverting) the # second lower-level multiplex) multiplex_2 = self._multiplex(target_gate, list_of_angles[(list_len // 2):]) if list_len > 1: circuit.append(multiplex_2.to_instruction().mirror(), q[0:-1]) else: circuit.append(multiplex_2.to_instruction(), q[0:-1]) # attach a final CNOT circuit.append(CnotGate(), [msb, lsb]) return circuit
def test_qregs_eq_invalid_type(self): """Test getting quantum registers from circuit.""" qr1 = QuantumRegister(10, "q") self.assertNotEqual(qr1, 3.14)
def test_apply_cx_to_non_register(self): """test applying ccx to non-register raises""" qr = QuantumRegister(10) cr = ClassicalRegister(10) qc = QuantumCircuit(qr, cr) self.assertRaises(CircuitError, qc.cx, qc[0:2], qc[2:4])
def test_apply_ccx_to_empty_slice(self): """test applying ccx to non-register raises""" qr = QuantumRegister(10) cr = ClassicalRegister(10) qc = QuantumCircuit(qr, cr) self.assertRaises(CircuitError, qc.ccx, qr[2:0], qr[4:2], qr[7:5])
def convert( self, operator: CircuitStateFn, params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], ) -> ListOp: r""" Args: operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` for which we compute the QFI. params: The parameters :math:`\omega` with respect to which we are computing the QFI. Returns: A ``ListOp[ListOp]`` where the operator at position ``[k][l]`` corresponds to the matrix element :math:`k, l` of the QFI. Raises: TypeError: If ``operator`` is an unsupported type. """ # QFI & phase fix observable qfi_observable = ~StateFn(4 * Z ^ (I ^ operator.num_qubits)) phase_fix_observable = StateFn( (Z + 1j * Y) ^ (I ^ operator.num_qubits), is_measurement=True) # see https://arxiv.org/pdf/quant-ph/0108146.pdf # Check if the given operator corresponds to a quantum state given as a circuit. if not isinstance(operator, CircuitStateFn): raise TypeError( "LinCombFull is only compatible with states that are given as " f"CircuitStateFn, not {type(operator)}") # If a single parameter is given wrap it into a list. if isinstance(params, ParameterExpression): params = [params] elif isinstance(params, ParameterVector): params = params[:] # unroll to list # First, the operators are computed which can compensate for a potential phase-mismatch # between target and trained state, i.e.〈ψ|∂lψ〉 gradient_states = LinComb()._gradient_states( operator, meas_op=phase_fix_observable, target_params=params, open_ctrl=False, trim_after_grad_gate=True, ) # if type(gradient_states) in [ListOp, SummedOp]: # pylint: disable=unidiomatic-typecheck if type(gradient_states) == ListOp: phase_fix_states = gradient_states.oplist else: phase_fix_states = [gradient_states] # Get 4 * Re[〈∂kψ|∂lψ] qfi_operators = [] # Add a working qubit qr_work = QuantumRegister(1, "work_qubit") state_qc = QuantumCircuit(*operator.primitive.qregs, qr_work) state_qc.h(qr_work) state_qc.compose(operator.primitive, inplace=True) # Get the circuits needed to compute〈∂iψ|∂jψ〉 for i, param_i in enumerate(params): # loop over parameters qfi_ops = [] for j, param_j in enumerate(params[i:], i): # Get the gates of the quantum state which are parameterized by param_i qfi_op = [] param_gates_i = state_qc._parameter_table[param_i] for gate_i, idx_i in param_gates_i: grad_coeffs_i, grad_gates_i = LinComb._gate_gradient_dict( gate_i)[idx_i] # get the location of gate_i, used for trimming location_i = None for idx, (op, _, _) in enumerate(state_qc._data): if op is gate_i: location_i = idx break for grad_coeff_i, grad_gate_i in zip( grad_coeffs_i, grad_gates_i): # Get the gates of the quantum state which are parameterized by param_j param_gates_j = state_qc._parameter_table[param_j] for gate_j, idx_j in param_gates_j: grad_coeffs_j, grad_gates_j = LinComb._gate_gradient_dict( gate_j)[idx_j] # get the location of gate_j, used for trimming location_j = None for idx, (op, _, _) in enumerate(state_qc._data): if op is gate_j: location_j = idx break for grad_coeff_j, grad_gate_j in zip( grad_coeffs_j, grad_gates_j): grad_coeff_ij = np.conj( grad_coeff_i) * grad_coeff_j qfi_circuit = LinComb.apply_grad_gate( state_qc, gate_i, idx_i, grad_gate_i, grad_coeff_ij, qr_work, open_ctrl=True, trim_after_grad_gate=(location_j < location_i), ) # create a copy of the original circuit with the same registers qfi_circuit = LinComb.apply_grad_gate( qfi_circuit, gate_j, idx_j, grad_gate_j, 1, qr_work, open_ctrl=False, trim_after_grad_gate=(location_j >= location_i), ) qfi_circuit.h(qr_work) # Convert the quantum circuit into a CircuitStateFn and add the # coefficients i, j and the original operator coefficient coeff = operator.coeff coeff *= np.sqrt( np.abs(grad_coeff_i) * np.abs(grad_coeff_j)) state = CircuitStateFn(qfi_circuit, coeff=coeff) param_grad = 1 for gate, idx, param in zip( [gate_i, gate_j], [idx_i, idx_j], [param_i, param_j]): param_expression = gate.params[idx] param_grad *= param_expression.gradient( param) meas = param_grad * qfi_observable term = meas @ state qfi_op.append(term) # Compute −4 * Re(〈∂kψ|ψ〉〈ψ|∂lψ〉) def phase_fix_combo_fn(x): return 4 * (-0.5) * (x[0] * np.conjugate(x[1]) + x[1] * np.conjugate(x[0])) phase_fix = ListOp([phase_fix_states[i], phase_fix_states[j]], combo_fn=phase_fix_combo_fn) # Add the phase fix quantities to the entries of the QFI # Get 4 * Re[〈∂kψ|∂lψ〉−〈∂kψ|ψ〉〈ψ|∂lψ〉] qfi_ops += [SummedOp(qfi_op) + phase_fix] qfi_operators.append(ListOp(qfi_ops)) # Return the full QFI return ListOp(qfi_operators, combo_fn=triu_to_dense)
def setUp(self): logger = getLogger() logger.setLevel('DEBUG') self.output = io.StringIO() logger.addHandler(StreamHandlerRaiseException(self.output)) self.circuit = QuantumCircuit(QuantumRegister(1))
def _define(self): """Calculate a subcircuit that implements this unitary.""" q = QuantumRegister(self.num_qubits, 'q') qc = QuantumCircuit(q, name=self.name) qc.append(UnitaryGate(self.to_matrix()), qargs=q[:]) self.definition = qc
def run(self, dag): """Return a new circuit that has been optimized.""" runs = dag.collect_runs(["u1", "u2", "u3"]) runs = _split_runs_on_parameters(runs) for run in runs: right_name = "u1" right_parameters = (0, 0, 0) # (theta, phi, lambda) for current_node in run: left_name = current_node.name if (current_node.condition is not None or len(current_node.qargs) != 1 or left_name not in ["u1", "u2", "u3", "id"]): raise TranspilerError("internal error") if left_name == "u1": left_parameters = (0, 0, current_node.op.params[0]) elif left_name == "u2": left_parameters = (np.pi / 2, current_node.op.params[0], current_node.op.params[1]) elif left_name == "u3": left_parameters = tuple(current_node.op.params) else: left_name = "u1" # replace id with u1 left_parameters = (0, 0, 0) # If there are any sympy objects coming from the gate convert # to numpy. left_parameters = tuple([float(x) for x in left_parameters]) # Compose gates name_tuple = (left_name, right_name) if name_tuple == ("u1", "u1"): # u1(lambda1) * u1(lambda2) = u1(lambda1 + lambda2) right_parameters = (0, 0, right_parameters[2] + left_parameters[2]) elif name_tuple == ("u1", "u2"): # u1(lambda1) * u2(phi2, lambda2) = u2(phi2 + lambda1, lambda2) right_parameters = (np.pi / 2, right_parameters[1] + left_parameters[2], right_parameters[2]) elif name_tuple == ("u2", "u1"): # u2(phi1, lambda1) * u1(lambda2) = u2(phi1, lambda1 + lambda2) right_name = "u2" right_parameters = (np.pi / 2, left_parameters[1], right_parameters[2] + left_parameters[2]) elif name_tuple == ("u1", "u3"): # u1(lambda1) * u3(theta2, phi2, lambda2) = # u3(theta2, phi2 + lambda1, lambda2) right_parameters = (right_parameters[0], right_parameters[1] + left_parameters[2], right_parameters[2]) elif name_tuple == ("u3", "u1"): # u3(theta1, phi1, lambda1) * u1(lambda2) = # u3(theta1, phi1, lambda1 + lambda2) right_name = "u3" right_parameters = (left_parameters[0], left_parameters[1], right_parameters[2] + left_parameters[2]) elif name_tuple == ("u2", "u2"): # Using Ry(pi/2).Rz(2*lambda).Ry(pi/2) = # Rz(pi/2).Ry(pi-2*lambda).Rz(pi/2), # u2(phi1, lambda1) * u2(phi2, lambda2) = # u3(pi - lambda1 - phi2, phi1 + pi/2, lambda2 + pi/2) right_name = "u3" right_parameters = (np.pi - left_parameters[2] - right_parameters[1], left_parameters[1] + np.pi / 2, right_parameters[2] + np.pi / 2) elif name_tuple[1] == "nop": right_name = left_name right_parameters = left_parameters else: # For composing u3's or u2's with u3's, use # u2(phi, lambda) = u3(pi/2, phi, lambda) # together with the qiskit.mapper.compose_u3 method. right_name = "u3" # Evaluate the symbolic expressions for efficiency right_parameters = Optimize1qGates.compose_u3( left_parameters[0], left_parameters[1], left_parameters[2], right_parameters[0], right_parameters[1], right_parameters[2]) # Why evalf()? This program: # OPENQASM 2.0; # include "qelib1.inc"; # qreg q[2]; # creg c[2]; # u3(0.518016983430947*pi,1.37051598592907*pi,1.36816383603222*pi) q[0]; # u3(1.69867232277986*pi,0.371448347747471*pi,0.461117217930936*pi) q[0]; # u3(0.294319836336836*pi,0.450325871124225*pi,1.46804720442555*pi) q[0]; # measure q -> c; # took >630 seconds (did not complete) to optimize without # calling evalf() at all, 19 seconds to optimize calling # evalf() AFTER compose_u3, and 1 second to optimize # calling evalf() BEFORE compose_u3. # 1. Here down, when we simplify, we add f(theta) to lambda to # correct the global phase when f(theta) is 2*pi. This isn't # necessary but the other steps preserve the global phase, so # we continue in that manner. # 2. The final step will remove Z rotations by 2*pi. # 3. Note that is_zero is true only if the expression is exactly # zero. If the input expressions have already been evaluated # then these final simplifications will not occur. # TODO After we refactor, we should have separate passes for # exact and approximate rewriting. # Y rotation is 0 mod 2*pi, so the gate is a u1 if np.mod(right_parameters[0], (2 * np.pi)) == 0 \ and right_name != "u1": right_name = "u1" right_parameters = (0, 0, right_parameters[1] + right_parameters[2] + right_parameters[0]) # Y rotation is pi/2 or -pi/2 mod 2*pi, so the gate is a u2 if right_name == "u3": # theta = pi/2 + 2*k*pi if np.mod((right_parameters[0] - np.pi / 2), (2 * np.pi)) == 0: right_name = "u2" right_parameters = (np.pi / 2, right_parameters[1], right_parameters[2] + (right_parameters[0] - np.pi / 2)) # theta = -pi/2 + 2*k*pi if np.mod((right_parameters[0] + np.pi / 2), (2 * np.pi)) == 0: right_name = "u2" right_parameters = (np.pi / 2, right_parameters[1] + np.pi, right_parameters[2] - np.pi + (right_parameters[0] + np.pi / 2)) # u1 and lambda is 0 mod 2*pi so gate is nop (up to a global phase) if right_name == "u1" and np.mod(right_parameters[2], (2 * np.pi)) == 0: right_name = "nop" # Replace the the first node in the run with a dummy DAG which contains a dummy # qubit. The name is irrelevant, because substitute_node_with_dag will take care of # putting it in the right place. run_qarg = QuantumRegister(1, 'q')[0] new_op = Gate(name="", num_qubits=1, params=[]) if right_name == "u1": new_op = U1Gate(right_parameters[2]) if right_name == "u2": new_op = U2Gate(right_parameters[1], right_parameters[2]) if right_name == "u3": new_op = U3Gate(*right_parameters) if right_name != 'nop': new_dag = DAGCircuit() new_dag.add_qreg(run_qarg.register) new_dag.apply_operation_back(new_op, [run_qarg], []) dag.substitute_node_with_dag(run[0], new_dag) # Delete the other nodes in the run for current_node in run[1:]: dag.remove_op_node(current_node) if right_name == "nop": dag.remove_op_node(run[0]) return dag
def qs_decomposition(mat, opt_a1=True, decomposer_1q=None, decomposer_2q=None): """ Decomposes unitary matrix into one and two qubit gates using Quantum Shannon Decomposition. ┌───┐ ┌───┐ ┌───┐ ┌───┐ ─┤ ├─ ───────┤ Rz├─────┤ Ry├─────┤ Rz├───── │ │ ≃ ┌───┐└─┬─┘┌───┐└─┬─┘┌───┐└─┬─┘┌───┐ /─┤ ├─ /─┤ ├──□──┤ ├──□──┤ ├──□──┤ ├ └───┘ └───┘ └───┘ └───┘ └───┘ The number of CX gates generated with the decomposition without optimizations is, .. math:: \frac{9}{16} 4^n - frac{3}{2} 2^n If opt_a1=True, the CX count is further reduced by, .. math:: \frac{1}{3} 4^{n - 2} - 1 This decomposition is described in arXiv:quant-ph/0406176. Arguments: mat (ndarray): unitary matrix to decompose opt_a1 (bool): whether to try optimization A.1 from Shende. This should eliminate 1 cnot per call. If True CZ gates are left in the output. If desired these can be further decomposed to CX. decomposer_1q (None or Object): optional 1Q decomposer. If None, uses :class:`~qiskit.quantum_info.synthesis.one_qubit_decomposer.OneQubitEulerDecomser` decomposer_2q (None or Object): optional 2Q decomposer. If None, uses :class:`~qiskit.quantum_info.synthesis.two_qubit_decomposer.TwoQubitBasisDecomposer` with CXGate. Return: QuantumCircuit: Decomposed quantum circuit. """ dim = mat.shape[0] nqubits = int(np.log2(dim)) if np.allclose(np.identity(dim), mat): return QuantumCircuit(nqubits) if dim == 2: if decomposer_1q is None: decomposer_1q = one_qubit_decompose.OneQubitEulerDecomposer() circ = decomposer_1q(mat) elif dim == 4: if decomposer_2q is None: decomposer_2q = two_qubit_decompose.TwoQubitBasisDecomposer(CXGate()) circ = decomposer_2q(mat) else: qr = QuantumRegister(nqubits) circ = QuantumCircuit(qr) dim_o2 = dim // 2 # perform cosine-sine decomposition (u1, u2), vtheta, (v1h, v2h) = scipy.linalg.cossin(mat, separate=True, p=dim_o2, q=dim_o2) # left circ left_circ = _demultiplex(v1h, v2h, opt_a1=opt_a1) circ.append(left_circ.to_instruction(), qr) # middle circ if opt_a1: nangles = len(vtheta) half_size = nangles // 2 # get UCG in terms of CZ circ_cz = _get_ucry_cz(nqubits, (2 * vtheta).tolist()) circ.append(circ_cz.to_instruction(), range(nqubits)) # merge final cz with right-side generic multiplexer u2[:, half_size:] = np.negative(u2[:, half_size:]) else: circ.ucry((2 * vtheta).tolist(), qr[:-1], qr[-1]) # right circ right_circ = _demultiplex(u1, u2, opt_a1=opt_a1) circ.append(right_circ.to_instruction(), qr) return circ
def run(self, dag): """iterate over each block and replace it with an equivalent Unitary on the same wires. """ new_dag = DAGCircuit() for qreg in dag.qregs.values(): new_dag.add_qreg(qreg) for creg in dag.cregs.values(): new_dag.add_creg(creg) # compute ordered indices for the global circuit wires global_index_map = {} for wire in dag.wires: if not isinstance(wire[0], QuantumRegister): continue global_qregs = list(dag.qregs.values()) global_index_map[wire] = global_qregs.index(wire[0]) + wire[1] blocks = self.property_set['block_list'] nodes_seen = set() for node in dag.topological_op_nodes(): # skip already-visited nodes or input/output nodes if node in nodes_seen or node.type == 'in' or node.type == 'out': continue # check if the node belongs to the next block if blocks and node in blocks[0]: block = blocks[0] # find the qubits involved in this block block_qargs = set() for nd in block: block_qargs |= set(nd.qargs) # convert block to a sub-circuit, then simulate unitary and add block_width = len(block_qargs) q = QuantumRegister(block_width) subcirc = QuantumCircuit(q) block_index_map = self._block_qargs_to_indices( block_qargs, global_index_map) for nd in block: nodes_seen.add(nd) subcirc.append(nd.op, [q[block_index_map[i]] for i in nd.qargs]) unitary = UnitaryGate( Operator(subcirc)) # simulates the circuit new_dag.apply_operation_back( unitary, sorted(block_qargs, key=lambda x: block_index_map[x])) del blocks[0] else: # the node could belong to some future block, but in that case # we simply skip it. It is guaranteed that we will revisit that # future block, via its other nodes for block in blocks[1:]: if node in block: break # freestanding nodes can just be added else: nodes_seen.add(node) new_dag.apply_operation_back(node.op, node.qargs, node.cargs) return new_dag
def num_qubits(self, num_qubits): self._invalidate() self._num_qubits = num_qubits self.qregs = [QuantumRegister(self.num_qubits, name="q")]
def _off_diag_circ(self, theta: float = 1) -> QuantumCircuit: """Circuit implementing the matrix consisting of entries in the off diagonals. Args: theta: Scale factor for the off diagonal entries (e.g. evolution_time/trotter_steps). Returns: The quantum circuit implementing the matrix consisting of entries in the off diagonals. """ theta *= self.off_diag qr = QuantumRegister(self.num_state_qubits) if self.num_state_qubits > 1: qr_ancilla = AncillaRegister(max(1, self.num_state_qubits - 2)) qc = QuantumCircuit(qr, qr_ancilla, name="off_diags") else: qc = QuantumCircuit(qr, name="off_diags") qr_ancilla = None qc.u(-2 * theta, 3 * np.pi / 2, np.pi / 2, qr[0]) for i in range(0, self.num_state_qubits - 1): q_controls = [] qc.cx(qr[i], qr[i + 1]) q_controls.append(qr[i + 1]) # Now we want controlled by 0 qc.x(qr[i]) for j in range(i, 0, -1): qc.cx(qr[i], qr[j - 1]) q_controls.append(qr[j - 1]) qc.x(qr[i]) # Multicontrolled rotation if len(q_controls) > 1: ugate = UGate(-2 * theta, 3 * np.pi / 2, np.pi / 2) qc.append( MCMTVChain(ugate, len(q_controls), 1), q_controls[:] + [qr[i]] + qr_ancilla[:len(q_controls) - 1], ) else: qc.cu(-2 * theta, 3 * np.pi / 2, np.pi / 2, 0, q_controls[0], qr[i]) # Uncompute qc.x(qr[i]) for j in range(0, i): qc.cx(qr[i], qr[j]) qc.x(qr[i]) qc.cx(qr[i], qr[i + 1]) # pylint: disable=unused-argument def control(num_ctrl_qubits=1, label=None, ctrl_state=None): qr_state = QuantumRegister(self.num_state_qubits + 1) if self.num_state_qubits > 1: qr_ancilla = AncillaRegister(max(1, self.num_state_qubits - 1)) qc_control = QuantumCircuit(qr_state, qr_ancilla, name="off_diags") else: qc_control = QuantumCircuit(qr_state, name="off_diags") qr_ancilla = None # Control will be qr[0] q_control = qr_state[0] qr = qr_state[1:] qc_control.cu(-2 * theta, 3 * np.pi / 2, np.pi / 2, 0, q_control, qr[0]) for i in range(0, self.num_state_qubits - 1): q_controls = [] q_controls.append(q_control) qc_control.cx(qr[i], qr[i + 1]) q_controls.append(qr[i + 1]) # Now we want controlled by 0 qc_control.x(qr[i]) for j in range(i, 0, -1): qc_control.cx(qr[i], qr[j - 1]) q_controls.append(qr[j - 1]) qc_control.x(qr[i]) # Multicontrolled x rotation if len(q_controls) > 1: ugate = UGate(-2 * theta, 3 * np.pi / 2, np.pi / 2) qc_control.append( MCMTVChain(ugate, len(q_controls), 1).to_gate(), q_controls[:] + [qr[i]] + qr_ancilla[:len(q_controls) - 1], ) else: qc_control.cu(-2 * theta, 3 * np.pi / 2, np.pi / 2, 0, q_controls[0], qr[i]) # Uncompute qc_control.x(qr[i]) for j in range(0, i): qc_control.cx(qr[i], qr[j]) qc_control.x(qr[i]) qc_control.cx(qr[i], qr[i + 1]) return qc_control qc.control = control return qc
def _compose_transforms(basis_transforms, source_basis, source_dag): """Compose a set of basis transforms into a set of replacements. Args: basis_transforms (List[Tuple[gate_name, params, equiv]]): List of transforms to compose. source_basis (Set[Tuple[gate_name: str, gate_num_qubits: int]]): Names of gates which need to be translated. source_dag (DAGCircuit): DAG with example gates from source_basis. (Used to determine num_params for gate in source_basis.) Returns: Dict[gate_name, Tuple(params, dag)]: Dictionary mapping between each gate in source_basis and a DAGCircuit instance to replace it. Gates in source_basis but not affected by basis_transforms will be included as a key mapping to itself. """ example_gates = {(node.op.name, node.op.num_qubits): node.op for node in source_dag.op_nodes()} mapped_instrs = {} for gate_name, gate_num_qubits in source_basis: # Need to grab a gate instance to find num_qubits and num_params. # Can be removed following https://github.com/Qiskit/qiskit-terra/pull/3947 . example_gate = example_gates[gate_name, gate_num_qubits] num_params = len(example_gate.params) placeholder_params = ParameterVector(gate_name, num_params) placeholder_gate = Gate(gate_name, gate_num_qubits, list(placeholder_params)) placeholder_gate.params = list(placeholder_params) dag = DAGCircuit() qr = QuantumRegister(gate_num_qubits) dag.add_qreg(qr) dag.apply_operation_back(placeholder_gate, qr[:], []) mapped_instrs[gate_name, gate_num_qubits] = placeholder_params, dag for gate_name, gate_num_qubits, equiv_params, equiv in basis_transforms: logger.debug( "Composing transform step: %s/%s %s =>\n%s", gate_name, gate_num_qubits, equiv_params, equiv, ) for mapped_instr_name, (dag_params, dag) in mapped_instrs.items(): doomed_nodes = [ node for node in dag.op_nodes() if (node.op.name, node.op.num_qubits) == (gate_name, gate_num_qubits) ] if doomed_nodes and logger.isEnabledFor(logging.DEBUG): from qiskit.converters import dag_to_circuit logger.debug( "Updating transform for mapped instr %s %s from \n%s", mapped_instr_name, dag_params, dag_to_circuit(dag), ) for node in doomed_nodes: from qiskit.converters import circuit_to_dag replacement = equiv.assign_parameters( dict(zip_longest(equiv_params, node.op.params)) ) replacement_dag = circuit_to_dag(replacement) dag.substitute_node_with_dag(node, replacement_dag) if doomed_nodes and logger.isEnabledFor(logging.DEBUG): from qiskit.converters import dag_to_circuit logger.debug( "Updated transform for mapped instr %s %s to\n%s", mapped_instr_name, dag_params, dag_to_circuit(dag), ) return mapped_instrs
def random_circuit(n_qubits, depth, coupling_map=None, basis_gates=None, prob_one_q_op=0.5, reset=False, seed=None): """Generate RQC faithfully according to the given coupling_map and basis_gates. Args: n_qubits (int): the number of qubits, must > largest qubit label in coupling_map depth (int): the number of the layers of operations coupling_map (CouplingMap or list): coupling map specifies allowed CNOTs default: fully connected graph coupling n_qubits basis_gates (list): the list of labels of basis gates used in construction default: all available gates in gate_set prob_one_q_op (float): the probability of selecting a one-qubit operation when two_q_op is allowed default: equal probability 0.5 reset (bool): if True, insert middle resets seed (int): sets random seed (optional) Returns: QuantumCircuit: constructed circuit Raises: CircuitError: when invalid options given """ max_operands = 2 assert max_operands == 2 if isinstance(coupling_map, list): coupling_map = CouplingMap(coupling_map) if coupling_map != None and n_qubits < max( coupling_map.physical_qubits) + 1: raise CircuitError("n_qubits is not enough to accomodate CouplingMap") if basis_gates == None: basis_gates = gate_set one_q_ops = [ label2gate[name] for name in one_q_ops_label & set(basis_gates) ] two_q_ops = [ label2gate[name] for name in two_q_ops_label & set(basis_gates) ] one_param = [ label2gate[name] for name in one_param_label & set(basis_gates) ] two_param = [ label2gate[name] for name in two_param_label & set(basis_gates) ] three_param = [ label2gate[name] for name in three_param_label & set(basis_gates) ] if len(one_q_ops) == 0: raise CircuitError("no available one-qubit gate") if len(two_q_ops) == 0: raise CircuitError("CNOT is not available") qreg = QuantumRegister(n_qubits, 'q') qc = QuantumCircuit(n_qubits) # default coupling_map is fully connected if coupling_map == None: coupling_map = CouplingMap.from_full(n_qubits) if reset: one_q_ops += [Reset] if seed is None: seed = np.random.randint(0, np.iinfo(np.int32).max) rng = np.random.RandomState(seed) for _ in range(depth): remaining_qubits = coupling_map.physical_qubits remaining_edges = coupling_map.get_edges() if remaining_edges: allow_two_q_op = True while remaining_qubits: if allow_two_q_op: max_possible_operands = min(len(remaining_qubits), max_operands) else: max_possible_operands = 1 if max_possible_operands == 1: possible_operands_set = [1] num_operands = 1 else: possible_operands_set = [1, 2] num_operands = (not (rng.uniform() < prob_one_q_op)) + 1 if num_operands == 1: operation = rng.choice(one_q_ops) operands = rng.choice(remaining_qubits) register_operands = [qreg[int(operands)]] operands = [operands] elif num_operands == 2: operation = rng.choice(two_q_ops) operands = remaining_edges[rng.choice( range(len(remaining_edges)))] register_operands = [qreg[i] for i in operands] remaining_qubits = [ q for q in remaining_qubits if q not in operands ] if remaining_edges: remaining_edges = [ pair for pair in remaining_edges if pair[0] not in operands and pair[1] not in operands ] if allow_two_q_op and not remaining_edges: allow_two_q_op = False if operation in one_param: num_angles = 1 elif operation in two_param: num_angles = 2 elif operation in three_param: num_angles = 3 else: num_angles = 0 angles = [rng.uniform(0, 2 * np.pi) for x in range(num_angles)] op = operation(*angles) qc.append(op, register_operands) return qc
def test_add_reg_duplicate(self): """add_qreg with the same register twice is not allowed.""" dag = DAGCircuit() qr = QuantumRegister(2) dag.add_qreg(qr) self.assertRaises(DAGCircuitError, dag.add_qreg, qr)
qtest_data_ft3=qtest_data['petal width (cm)'] qtest_data_ft4=[] qtest_data_ft4=qtest_data['petal width (cm)'] # In[66]: from qiskit.quantum_info import state_fidelity # In[67]: device=Aer.get_backend('qasm_simulator') q=QuantumRegister(5) c=ClassicalRegister(5) cir1=QuantumCircuit(q,c) cir1.u3(qtrain_data_ft1[2],qtrain_data_ft2[2],0,q[1]) #quantum state training data ft1&ft2 cir1.u3(qtrain_data_ft3[2],qtrain_data_ft4[2],0,q[2]) #quantum state training data ft3&ft4 cir1.u3(qtest_data_ft1[3],qtest_data_ft2[3],0,q[3]) #quantum state testing data ft1&ft2 cir1.u3(qtest_data_ft3[3],qtest_data_ft4[3],0,q[4]) #quantum state testing data ft3&ft4 cir1.barrier() cir1.h(q[0]) cir1.cswap(q[0],q[1],q[3]) cir1.cswap(q[0],q[2],q[4]) cir1.h(q[0]) cir1.measure(q[0],c[0]) cir1.draw(output='mpl')
def _process_node(self, node): """Carry out the action associated with a node.""" if node.type == "program": self._process_children(node) elif node.type == "qreg": qreg = QuantumRegister(node.index, node.name) self.dag.add_qreg(qreg) elif node.type == "creg": creg = ClassicalRegister(node.index, node.name) self.dag.add_creg(creg) elif node.type == "id": raise QiskitError("internal error: _process_node on id") elif node.type == "int": raise QiskitError("internal error: _process_node on int") elif node.type == "real": raise QiskitError("internal error: _process_node on real") elif node.type == "indexed_id": raise QiskitError("internal error: _process_node on indexed_id") elif node.type == "id_list": # We process id_list nodes when they are leaves of barriers. return [self._process_bit_id(node_children) for node_children in node.children] elif node.type == "primary_list": # We should only be called for a barrier. return [self._process_bit_id(m) for m in node.children] elif node.type == "gate": self._process_gate(node) elif node.type == "custom_unitary": self._process_custom_unitary(node) elif node.type == "universal_unitary": args = self._process_node(node.children[0]) qid = self._process_bit_id(node.children[1]) for element in qid: u3_gate = U3Gate(*args, element) u3_gate.condition = self.condition self.dag.apply_operation_back(u3_gate) elif node.type == "cnot": self._process_cnot(node) elif node.type == "expression_list": return node.children elif node.type == "binop": raise QiskitError("internal error: _process_node on binop") elif node.type == "prefix": raise QiskitError("internal error: _process_node on prefix") elif node.type == "measure": self._process_measure(node) elif node.type == "format": self.version = node.version() elif node.type == "barrier": ids = self._process_node(node.children[0]) qubits = [] for qubit in ids: for j, _ in enumerate(qubit): qubits.append(qubit[j]) self.dag.apply_operation_back(Barrier(len(qubits)), qubits, []) elif node.type == "reset": id0 = self._process_bit_id(node.children[0]) for i, _ in enumerate(id0): reset = Reset() reset.condition = self.condition self.dag.apply_operation_back(reset, [id0[i]], []) elif node.type == "if": self._process_if(node) elif node.type == "opaque": self._process_gate(node, opaque=True) elif node.type == "external": raise QiskitError("internal error: _process_node on external") else: raise QiskitError("internal error: undefined node type", node.type, "line=%s" % node.line, "file=%s" % node.file) return None
def construct_circuit( self, matrix: Union[np.ndarray, QuantumCircuit], vector: Union[np.ndarray, QuantumCircuit]) -> QuantumCircuit: """Construct the HHL circuit. Args: matrix: The matrix specifying the system, i.e. A in Ax=b. vector: The vector specifying the right hand side of the equation in Ax=b. Returns: The HHL circuit. Raises: ValueError: If the input is not in the correct format. ValueError: If the type of the input matrix is not supported. """ # State preparation circuit - default is qiskit if isinstance(vector, QuantumCircuit): nb = vector.num_qubits vector_circuit = vector elif isinstance(vector, np.ndarray): nb = int(np.log2(len(vector))) vector_circuit = QuantumCircuit(nb) vector_circuit.isometry(vector / np.linalg.norm(vector), list(range(nb)), None) # If state preparation is probabilistic the number of qubit flags should increase nf = 1 # Hamiltonian simulation circuit - default is Trotterization if isinstance(matrix, QuantumCircuit): matrix_circuit = matrix elif isinstance(matrix, (list, np.ndarray)): if isinstance(matrix, list): matrix = np.array(matrix) if matrix.shape[0] != matrix.shape[1]: raise ValueError("Input matrix must be square!") if np.log2(matrix.shape[0]) % 1 != 0: raise ValueError("Input matrix dimension must be 2^n!") if not np.allclose(matrix, matrix.conj().T): raise ValueError("Input matrix must be hermitian!") if matrix.shape[0] != 2**vector_circuit.num_qubits: raise ValueError("Input vector dimension does not match input " "matrix dimension! Vector dimension: " + str(vector_circuit.num_qubits) + ". Matrix dimension: " + str(matrix.shape[0])) matrix_circuit = NumPyMatrix(matrix, evolution_time=2 * np.pi) else: raise ValueError(f'Invalid type for matrix: {type(matrix)}.') # Set the tolerance for the matrix approximation if hasattr(matrix_circuit, "tolerance"): matrix_circuit.tolerance = self._epsilon_a # check if the matrix can calculate the condition number and store the upper bound if hasattr(matrix_circuit, "condition_bounds") and matrix_circuit.condition_bounds() is not\ None: kappa = matrix_circuit.condition_bounds()[1] else: kappa = 1 # Update the number of qubits required to represent the eigenvalues nl = max(nb + 1, int(np.log2(kappa)) + 1) # check if the matrix can calculate bounds for the eigenvalues if hasattr(matrix_circuit, "eigs_bounds") and matrix_circuit.eigs_bounds() is not None: lambda_min, lambda_max = matrix_circuit.eigs_bounds() # Constant so that the minimum eigenvalue is represented exactly, since it contributes # the most to the solution of the system delta = self._get_delta(nl, lambda_min, lambda_max) # Update evolution time matrix_circuit.evolution_time = 2 * np.pi * delta / lambda_min # Update the scaling of the solution self.scaling = lambda_min else: delta = 1 / (2**nl) print("The solution will be calculated up to a scaling factor.") if self._exact_reciprocal: reciprocal_circuit = ExactReciprocal(nl, delta) # Update number of ancilla qubits na = matrix_circuit.num_ancillas else: # Calculate breakpoints for the reciprocal approximation num_values = 2**nl constant = delta a = int(round(num_values**(2 / 3))) # pylint: disable=invalid-name # Calculate the degree of the polynomial and the number of intervals r = 2 * constant / a + np.sqrt(np.abs(1 - (2 * constant / a)**2)) degree = min( nb, int( np.log(1 + (16.23 * np.sqrt(np.log(r)**2 + (np.pi / 2)**2) * kappa * (2 * kappa - self._epsilon_r)) / self._epsilon_r))) num_intervals = int( np.ceil(np.log((num_values - 1) / a) / np.log(5))) # Calculate breakpoints and polynomials breakpoints = [] for i in range(0, num_intervals): # Add the breakpoint to the list breakpoints.append(a * (5**i)) # Define the right breakpoint of the interval if i == num_intervals - 1: breakpoints.append(num_values - 1) reciprocal_circuit = PiecewiseChebyshev( lambda x: np.arcsin(constant / x), degree, breakpoints, nl) na = max(matrix_circuit.num_ancillas, reciprocal_circuit.num_ancillas) # Initialise the quantum registers qb = QuantumRegister(nb) # right hand side and solution ql = QuantumRegister(nl) # eigenvalue evaluation qubits if na > 0: qa = AncillaRegister(na) # ancilla qubits qf = QuantumRegister(nf) # flag qubits if na > 0: qc = QuantumCircuit(qb, ql, qa, qf) else: qc = QuantumCircuit(qb, ql, qf) # State preparation qc.append(vector_circuit, qb[:]) # QPE phase_estimation = PhaseEstimation(nl, matrix_circuit) if na > 0: qc.append(phase_estimation, ql[:] + qb[:] + qa[:matrix_circuit.num_ancillas]) else: qc.append(phase_estimation, ql[:] + qb[:]) # Conditioned rotation if self._exact_reciprocal: qc.append(reciprocal_circuit, ql[::-1] + [qf[0]]) else: qc.append(reciprocal_circuit.to_instruction(), ql[:] + [qf[0]] + qa[:reciprocal_circuit.num_ancillas]) # QPE inverse if na > 0: qc.append(phase_estimation.inverse(), ql[:] + qb[:] + qa[:matrix_circuit.num_ancillas]) else: qc.append(phase_estimation.inverse(), ql[:] + qb[:]) return qc
def random_circuit(n_qubits, depth, max_operands=3, measure=False, conditional=False, reset=False, seed=None): """Generate random circuit of arbitrary size and form. Args: n_qubits (int): number of quantum wires depth (int): layers of operations (i.e. critical path length) max_operands (int): maximum operands of each gate (between 1 and 3) measure (bool): if True, measure all qubits at the end conditional (bool): if True, insert middle measurements and conditionals reset (bool): if True, insert middle resets seed (int): sets random seed (optional) Returns: QuantumCircuit: constructed circuit Raises: CircuitError: when invalid options given """ if max_operands < 1 or max_operands > 3: raise CircuitError("max_operands must be between 1 and 3") one_q_ops = [ IGate, U1Gate, U2Gate, U3Gate, XGate, YGate, ZGate, HGate, SGate, SdgGate, TGate, TdgGate, RXGate, RYGate, RZGate ] one_param = [U1Gate, RXGate, RYGate, RZGate, RZZGate, CU1Gate, CRZGate] two_param = [U2Gate] three_param = [U3Gate, CU3Gate] two_q_ops = [ CXGate, CYGate, CZGate, CHGate, CRZGate, CU1Gate, CU3Gate, SwapGate, RZZGate ] three_q_ops = [CCXGate, CSwapGate] qr = QuantumRegister(n_qubits, 'q') qc = QuantumCircuit(n_qubits) if measure or conditional: cr = ClassicalRegister(n_qubits, 'c') qc.add_register(cr) if reset: one_q_ops += [Reset] if seed is None: seed = np.random.randint(0, np.iinfo(np.int32).max) rng = np.random.RandomState(seed) # apply arbitrary random operations at every depth for _ in range(depth): # choose either 1, 2, or 3 qubits for the operation remaining_qubits = list(range(n_qubits)) while remaining_qubits: max_possible_operands = min(len(remaining_qubits), max_operands) num_operands = rng.choice(range(max_possible_operands)) + 1 rng.shuffle(remaining_qubits) operands = remaining_qubits[:num_operands] remaining_qubits = [ q for q in remaining_qubits if q not in operands ] if num_operands == 1: operation = rng.choice(one_q_ops) elif num_operands == 2: operation = rng.choice(two_q_ops) elif num_operands == 3: operation = rng.choice(three_q_ops) if operation in one_param: num_angles = 1 elif operation in two_param: num_angles = 2 elif operation in three_param: num_angles = 3 else: num_angles = 0 angles = [rng.uniform(0, 2 * np.pi) for x in range(num_angles)] register_operands = [qr[i] for i in operands] op = operation(*angles) # with some low probability, condition on classical bit values if conditional and rng.choice(range(10)) == 0: value = rng.randint(0, np.power(2, n_qubits)) op.condition = (cr, value) qc.append(op, register_operands) if measure: qc.measure(qr, cr) return qc
def test_basic_slice(self): """simple slice test""" qr = QuantumRegister(5) cr = ClassicalRegister(5) self.assertEqual(len(qr[0:3]), 3) self.assertEqual(len(cr[0:3]), 3)