def wrapper(self, *args): """Wrapper for control-target gate""" params = args[0:-2] if len(args) > 2 else tuple() ctl = args[-2] tgt = args[-1] if isinstance(ctl, QuantumRegister): ctl = [(ctl, i) for i in range(len(ctl))] if isinstance(tgt, QuantumRegister): tgt = [(tgt, i) for i in range(len(tgt))] if isinstance(ctl, list) != isinstance(tgt, list): # TODO: check for Qubit instance if isinstance(ctl, tuple): ctl = [ctl] elif isinstance(tgt, tuple): tgt = [tgt] else: raise ReNomQError('control or target are not qubits') if ctl and tgt and isinstance(ctl, list) and isinstance(tgt, list): instructions = InstructionSet() if len(ctl) == len(tgt): for ictl, itgt in zip(ctl, tgt): instructions.add(func(self, *params, ictl, itgt)) elif len(ctl) == 1: for itgt in tgt: instructions.add(func(self, *params, ctl[0], itgt)) elif len(tgt) == 1: for ictl in ctl: instructions.add(func(self, *params, ictl, tgt[0])) else: raise ReNomQError('indeterminate control or target qubits') return instructions return func(self, *params, ctl, tgt)
def _process_bit_id(self, node): """Process an Id or IndexedId node as a bit or register type. Return a list of tuples (Register,index). """ # pylint: disable=inconsistent-return-statements reg = None if node.name in self.dag.qregs: reg = self.dag.qregs[node.name] elif node.name in self.dag.cregs: reg = self.dag.cregs[node.name] else: raise ReNomQError("expected qreg or creg name:", "line=%s" % node.line, "file=%s" % node.file) if node.type == "indexed_id": # An indexed bit or qubit return [(reg, node.index)] elif node.type == "id": # A qubit or qreg or creg if not self.bit_stack[-1]: # Global scope return [(reg, j) for j in range(reg.size)] else: # local scope if node.name in self.bit_stack[-1]: return [self.bit_stack[-1][node.name]] raise ReNomQError("expected local bit name:", "line=%s" % node.line, "file=%s" % node.file) return None
def _check_qreg(self, register): """Raise exception if r is not in this circuit or not qreg.""" if not isinstance(register, QuantumRegister): raise ReNomQError("expected quantum register") if not self.has_register(register): raise ReNomQError("register '%s' not in this circuit" % register.name)
def _check_creg(self, register): """Raise exception if r is not in this circuit or not creg.""" if not isinstance(register, ClassicalRegister): raise ReNomQError("Expected ClassicalRegister, but %s given" % type(register)) if not self.has_register(register): raise ReNomQError("register '%s' not in this circuit" % register.name)
def add_register(self, *regs): """Add registers.""" for register in regs: if register in self.qregs or register in self.cregs: raise ReNomQError("register name \"%s\" already exists" % register.name) if isinstance(register, QuantumRegister): self.qregs.append(register) elif isinstance(register, ClassicalRegister): self.cregs.append(register) else: raise ReNomQError("expected a register")
def _check_qubit(self, qubit): """Raise exception if qubit is not in this circuit or bad format.""" if not isinstance(qubit, tuple): raise ReNomQError("%s is not a tuple." "A qubit should be formated as a tuple." % str(qubit)) if not len(qubit) == 2: raise ReNomQError( "%s is not a tuple with two elements, but %i instead" % len(qubit)) if not isinstance(qubit[1], int): raise ReNomQError( "The second element of a tuple defining a qubit should be an int:" "%s was found instead" % type(qubit[1]).__name__) self._check_qreg(qubit[0]) qubit[0].check_range(qubit[1])
def __init__(self, *regs, name=None): """Create a new circuit. A circuit is a list of instructions bound to some registers. Args: *regs (Registers): registers to include in the circuit. name (str or None): the name of the quantum circuit. If None, an automatically generated string will be assigned. Raises: ReNomQError: if the circuit name, if given, is not valid. """ if name is None: name = self.cls_prefix() + str(self.cls_instances()) # pylint: disable=not-callable # (known pylint bug: https://github.com/PyCQA/pylint/issues/1699) if sys.platform != "win32" and \ isinstance(mp.current_process(), mp.context.ForkProcess): name += '-{}'.format(mp.current_process().pid) self._increment_instances() if not isinstance(name, str): raise ReNomQError("The circuit name should be a string " "(or None to auto-generate a name).") self.name = name # Data contains a list of instructions in the order they were applied. self.data = [] # This is a map of registers bound to this circuit, by name. self.qregs = [] self.cregs = [] self.add_register(*regs)
def cswap(self, ctl, tgt1, tgt2): """Apply Fredkin to circuit.""" if isinstance(ctl, QuantumRegister): ctl = [(ctl, i) for i in range(len(ctl))] if isinstance(tgt1, QuantumRegister): tgt1 = [(tgt1, i) for i in range(len(tgt1))] if isinstance(tgt2, QuantumRegister): tgt2 = [(tgt2, i) for i in range(len(tgt2))] if ctl and tgt1 and tgt2: if isinstance(ctl, list) and \ isinstance(tgt1, list) and \ isinstance(tgt2, list): if len(ctl) == len(tgt1) and len(ctl) == len(tgt2): instructions = InstructionSet() for ictl, itgt1, itgt2 in zip(ctl, tgt1, tgt2): instructions.add(self.cswap(ictl, itgt1, itgt2)) return instructions else: raise ReNomQError('unequal register sizes') self._check_qubit(ctl) self._check_qubit(tgt1) self._check_qubit(tgt2) self._check_dups([ctl, tgt1, tgt2]) return self._attach(FredkinGate(ctl, tgt1, tgt2, self))
def wrapper(self, *args): """Wrapper for one qubit gate""" params = args[0:-2] if len(args) > 2 else tuple() qubit1 = args[-2] qubit2 = args[-1] if isinstance(qubit1, QuantumRegister): qubit1 = [(qubit1, j) for j in range(len(qubit1))] if isinstance(qubit2, QuantumRegister): qubit2 = [(qubit2, j) for j in range(len(qubit2))] if isinstance(qubit1, list) and isinstance(qubit2, list): if len(qubit1) != len(qubit2): raise ReNomQError('lengths of qubit arguments do not match: ' '{0} != {1}'.format(len(qubit1), len(qubit2))) if qubit1 and qubit2 and isinstance(qubit1, list) and isinstance( qubit2, list): instructions = InstructionSet() for iqubit1, iqubit2 in zip(qubit1, qubit2): self._check_qubit(iqubit1) self._check_qubit(iqubit2) instructions.add(func(self, *params, iqubit1, iqubit2)) return instructions return func(self, *params, qubit1, qubit2)
def _check_qubit(self, qubit): """Raise exception if q is not an argument or not qreg in circuit.""" self.check_circuit() self.circuit._check_qubit(qubit) if (qubit[0].name, qubit[1]) not in map(lambda x: (x[0].name, x[1]), self.qargs): raise ReNomQError("qubit '%s[%d]' not argument of gate" % (qubit[0].name, qubit[1]))
def _modifiers(self, gate): """Apply any modifiers of this instruction to another one.""" if self.control is not None: self.check_circuit() if not gate.circuit.has_register(self.control[0]): raise ReNomQError("control register %s not found" % self.control[0].name) gate.c_if(self.control[0], self.control[1])
def c_if(self, classical, val): """Add classical control on register classical and value val.""" self.check_circuit() self.circuit._check_creg(classical) if val < 0: raise ReNomQError("control value should be non-negative") self.control = (classical, val) return self
def _check_dups(self, qubits): """Raise exception. if list of qubits contains duplicates. """ squbits = set(qubits) if len(squbits) != len(qubits): raise ReNomQError("duplicate qubit arguments")
def _process_measure(self, node): """Process a measurement node.""" id0 = self._process_bit_id(node.children[0]) id1 = self._process_bit_id(node.children[1]) if len(id0) != len(id1): raise ReNomQError("internal error: reg size mismatch", "line=%s" % node.line, "file=%s" % node.file) for idx, idy in zip(id0, id1): self.dag.apply_operation_back(Measure(idx, idy), self.condition)
def __init__(self, name, params, qargs, cargs, circuit=None): """Create a new instruction. Args: name (str): instruction name params (list[sympy.Basic|qasm.Node|int|float|complex|str]): list of parameters qargs (list[(QuantumRegister, index)]): list of quantum args cargs (list[(ClassicalRegister, index)]): list of classical args circuit (QuantumCircuit or Instruction): where the instruction is attached Raises: ReNomQError: when the register is not in the correct format. """ if not all( (type(i[0]), type(i[1])) == (QuantumRegister, int) for i in qargs): raise ReNomQError("qarg not (QuantumRegister, int) tuple") if not all((type(i[0]), type(i[1])) == (ClassicalRegister, int) for i in cargs): raise ReNomQError("carg not (ClassicalRegister, int) tuple") self.name = name self.params = [] # a list of gate params stored as sympy objects for single_param in params: # example: u2(pi/2, sin(pi/4)) if isinstance(single_param, sympy.Basic): self.params.append(single_param) # example: OpenQASM parsed instruction elif isinstance(single_param, _node.Node): self.params.append(single_param.sym()) # example: u3(0.1, 0.2, 0.3) elif isinstance(single_param, (int, float)): self.params.append(sympy.Number(single_param)) # example: Initialize([complex(0,1), complex(0,0)]) elif isinstance(single_param, complex): self.params.append(single_param.real + single_param.imag * sympy.I) # example: snapshot('label') elif isinstance(single_param, str): self.params.append(sympy.Symbol(single_param)) else: raise ReNomQError("invalid param type {0} in instruction " "{1}".format(type(single_param), name)) self.qargs = qargs self.cargs = cargs self.control = None # tuple (ClassicalRegister, int) for "if" self.circuit = circuit
def __init__(self, size, name=None): """Create a new generic register. """ if name is None: name = '%s%i' % (self.prefix, next(self.instances_counter)) if not isinstance(name, str): raise ReNomQError("The circuit name should be a string " "(or None for autogenerate a name).") test = re.compile('[a-z][a-zA-Z0-9_]*') if test.match(name) is None: raise ReNomQError("%s is an invalid OPENQASM register name." % name) self.name = name self.size = size if size <= 0: raise ReNomQError("register size must be positive")
def _check_compatible_regs(self, rhs): """Raise exception if the circuits are defined on incompatible registers""" list1 = self.qregs + self.cregs list2 = rhs.qregs + rhs.cregs for element1 in list1: for element2 in list2: if element2.name == element1.name: if element1 != element2: raise ReNomQError("circuits are not compatible")
def measure(self, qubit, cbit): """Measure quantum bit into classical bit (tuples). Args: qubit (QuantumRegister|list|tuple): quantum register cbit (ClassicalRegister|list|tuple): classical register Returns: ReNomQ.Instruction: the attached measure instruction. Raises: ReNomQError: if qubit is not in this circuit or bad format; if cbit is not in this circuit or not creg. """ if isinstance(qubit, QuantumRegister): qubit = [(qubit, i) for i in range(len(qubit))] if isinstance(cbit, ClassicalRegister): cbit = [(cbit, i) for i in range(len(cbit))] if isinstance(qubit, list) != isinstance(cbit, list): # TODO: check for Qubit instance if isinstance(qubit, tuple): qubit = [qubit] elif isinstance(cbit, tuple): cbit = [cbit] else: raise ReNomQError('control or target are not qubits') if qubit and cbit and isinstance(qubit, list) and isinstance(cbit, list): if len(qubit) != len(cbit): raise ReNomQError( 'qubit and cbit should have the same length if lists') instructions = InstructionSet() for qb1, cb1 in zip(qubit, cbit): instructions.add(self.measure(qb1, cb1)) return instructions self._check_qubit(qubit) self._check_creg(cbit[0]) cbit[0].check_range(cbit[1]) return self._attach(Measure(qubit, cbit, self))
def ccx(self, ctl1, ctl2, tgt): """Apply Toffoli to from ctl1 and ctl2 to tgt.""" # expand registers to lists of qubits if isinstance(ctl1, QuantumRegister): ctl1 = [(ctl1, i) for i in range(len(ctl1))] if isinstance(ctl2, QuantumRegister): ctl2 = [(ctl2, i) for i in range(len(ctl2))] if isinstance(tgt, QuantumRegister): tgt = [(tgt, i) for i in range(len(tgt))] # expand single qubit target if controls are lists of qubits if isinstance(ctl1, list) and len(ctl1) == len(ctl2): if isinstance(tgt, tuple): tgt = [tgt] if len(tgt) == 1: tgt = tgt * len(ctl1) elif len(tgt) != len(ctl1): raise ReNomQError( 'target register size should match controls or be one') if ctl1 and ctl2 and tgt: if isinstance(ctl1, list) and \ isinstance(ctl2, list) and \ isinstance(tgt, list): if len(ctl1) == len(tgt) and len(ctl2) == len(tgt): instructions = InstructionSet() for ictl1, ictl2, itgt in zip(ctl1, ctl2, tgt): instructions.add(self.ccx(ictl1, ictl2, itgt)) return instructions else: raise ReNomQError('unequal register sizes') else: raise ReNomQError('empty control or target argument') self._check_qubit(ctl1) self._check_qubit(ctl2) self._check_qubit(tgt) self._check_dups([ctl1, ctl2, tgt]) return self._attach(ToffoliGate(ctl1, ctl2, tgt, self))
def _process_cnot(self, node): """Process a CNOT gate node.""" id0 = self._process_bit_id(node.children[0]) id1 = self._process_bit_id(node.children[1]) if not (len(id0) == len(id1) or len(id0) == 1 or len(id1) == 1): raise ReNomQError("internal error: qreg size mismatch", "line=%s" % node.line, "file=%s" % node.file) maxidx = max([len(id0), len(id1)]) for idx in range(maxidx): if len(id0) > 1 and len(id1) > 1: self.dag.apply_operation_back(CXBase(id0[idx], id1[idx]), self.condition) elif len(id0) > 1: self.dag.apply_operation_back(CXBase(id0[idx], id1[0]), self.condition) else: self.dag.apply_operation_back(CXBase(id0[0], id1[idx]), self.condition)
def __getitem__(self, key): """ Arg: key (int): index of the bit/qubit to be retrieved. Returns: tuple[Register, int]: a tuple in the form `(self, key)` if key is int. If key is a slice, return a `list((self,key))`. Raises: ReNomQError: if the `key` is not an integer. ReNomQIndexError: if the `key` is not in the range `(0, self.size)`. """ if not isinstance(key, (int, slice)): raise ReNomQError("expected integer or slice index into register") self.check_range(key) if isinstance(key, slice): return [(self, ind) for ind in range(*key.indices(len(self)))] else: return self, key
def _process_custom_unitary(self, node): """Process a custom unitary node.""" name = node.name if node.arguments is not None: args = self._process_node(node.arguments) else: args = [] bits = [ self._process_bit_id(node_element) for node_element in node.bitlist.children ] if name in self.gates: gargs = self.gates[name]["args"] gbits = self.gates[name]["bits"] # Loop over register arguments, if any. maxidx = max(map(len, bits)) for idx in range(maxidx): self.arg_stack.append( {gargs[j]: args[j] for j in range(len(gargs))}) # Only index into register arguments. element = [ idx * x for x in [len(bits[j]) > 1 for j in range(len(bits))] ] self.bit_stack.append( {gbits[j]: bits[j][element[j]] for j in range(len(gbits))}) self._create_dag_op( name, [self.arg_stack[-1][s].sym() for s in gargs], [self.bit_stack[-1][s] for s in gbits]) self.arg_stack.pop() self.bit_stack.pop() else: raise ReNomQError("internal error undefined gate:", "line=%s" % node.line, "file=%s" % node.file)
def check_circuit(self): """Raise exception if self.circuit is None.""" if self.circuit is None: raise ReNomQError("Instruction's circuit not assigned")
def q_if(self, *qregs): """Add controls to this gate.""" # pylint: disable=unused-argument raise ReNomQError("control not implemented")
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 ReNomQError("internal error: _process_node on id") elif node.type == "int": raise ReNomQError("internal error: _process_node on int") elif node.type == "real": raise ReNomQError("internal error: _process_node on real") elif node.type == "indexed_id": raise ReNomQError("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: self.dag.apply_operation_back(UBase(*args, element), self.condition) elif node.type == "cnot": self._process_cnot(node) elif node.type == "expression_list": return node.children elif node.type == "binop": raise ReNomQError("internal error: _process_node on binop") elif node.type == "prefix": raise ReNomQError("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(qubits)) elif node.type == "reset": id0 = self._process_bit_id(node.children[0]) for i, _ in enumerate(id0): self.dag.apply_operation_back(Reset(id0[i]), self.condition) elif node.type == "if": self._process_if(node) elif node.type == "opaque": self._process_gate(node, opaque=True) elif node.type == "external": raise ReNomQError("internal error: _process_node on external") else: raise ReNomQError("internal error: undefined node type", node.type, "line=%s" % node.line, "file=%s" % node.file) return None
def add(self, gate): """Add instruction to set.""" if not isinstance(gate, Instruction): raise ReNomQError("attempt to add non-Instruction" + " to InstructionSet") self.instructions.append(gate)
def inverse(self): """Invert this gate.""" raise ReNomQError("inverse not implemented")
def _create_dag_op(self, name, params, qargs): """ Create a DAG node out of a parsed AST op node. Args: name (str): operation name to apply to the dag. params (list): op parameters qargs (list(QuantumRegister, int)): qubits to attach to Raises: ReNomQError: if encountering a non-basis opaque gate """ if name == "u0": op_class = U0Gate elif name == "u1": op_class = U1Gate elif name == "u2": op_class = U2Gate elif name == "u3": op_class = U3Gate elif name == "x": op_class = XGate elif name == "y": op_class = YGate elif name == "z": op_class = ZGate elif name == "t": op_class = TGate elif name == "tdg": op_class = TdgGate elif name == "s": op_class = SGate elif name == "sdg": op_class = SdgGate elif name == "swap": op_class = SwapGate elif name == "rx": op_class = RXGate elif name == "ry": op_class = RYGate elif name == "rz": op_class = RZGate elif name == "rzz": op_class = RZZGate elif name == "id": op_class = IdGate elif name == "h": op_class = HGate elif name == "cx": op_class = CnotGate elif name == "cy": op_class = CyGate elif name == "cz": op_class = CzGate elif name == "ch": op_class = CHGate elif name == "crz": op_class = CrzGate elif name == "cu1": op_class = Cu1Gate elif name == "cu3": op_class = Cu3Gate elif name == "ccx": op_class = ToffoliGate elif name == "cswap": op_class = FredkinGate else: raise ReNomQError("unknown operation for ast node name %s" % name) op = op_class(*params, *qargs) self.dag.add_basis_element(name, len(qargs), 0, len(params)) self.dag.apply_operation_back(op, condition=self.condition)