def compile_controlled_rotation(gate: RotationGateImpl, angles: list = None) -> QCircuit: """ Recompilation of a controlled-rotation gate Basis change into Rz then recompilation of controled Rz, then change basis back :param gate: The rotational gate :param angles: new angles to set, given as a list of two. If None the angle in the gate is used (default) :return: set of gates wrapped in QCircuit class """ if not gate.is_controlled(): return QCircuit.wrap_gate(gate) if not isinstance(gate, RotationGateImpl): return QCircuit.wrap_gate(gate) if angles is None: angles = [gate.parameter / 2, -gate.parameter / 2] if len(gate.target) > 1: return compile_controlled_rotation(gate=compile_multitarget(gate=gate), angles=angles) target = gate.target control = gate.control result = QCircuit() result += change_basis(target=target, axis=gate._axis) result += RotationGateImpl(axis="z", target=target, angle=angles[0]) result += QGateImpl(name="X", target=target, control=control) result += RotationGateImpl(axis="Z", target=target, angle=angles[1]) result += QGateImpl(name="X", target=target, control=control) result += change_basis(target=target, axis=gate._axis, daggered=True) result.n_qubits = result.max_qubit() + 1 return result
def compile_controlled_rotation(gate: RotationGateImpl) -> QCircuit: """ Recompilation of a controlled-rotation gate Basis change into Rz then recompilation of controled Rz, then change basis back :param gate: The rotational gate :return: set of gates wrapped in QCircuit class """ if not gate.is_controlled(): return QCircuit.wrap_gate(gate) if not isinstance(gate, RotationGateImpl): return QCircuit.wrap_gate(gate) if len(gate.target) > 1: return compile_controlled_rotation(gate=compile_multitarget(gate=gate)) target = gate.target control = gate.control k = len(control) cind = _pattern(k) + [k - 1] result = QCircuit() result += change_basis(target=target, axis=gate._axis) coeff = -1 / pow(2, k) for i, ci in enumerate(cind): coeff *= -1 result += Rz(target=target, angle=coeff * gate.parameter) result += CNOT(control[ci], target) result += change_basis(target=target, axis=gate._axis, daggered=True) result.n_qubits = result.max_qubit() + 1 return result
def compile_controlled_power(gate: PowerGateImpl) -> QCircuit: """ Recompilation of a controlled-power gate Basis change into Z then recompilation of controled Z, then change basis back :param gate: The power gate :return: set of gates wrapped in QCircuit class """ if not gate.is_controlled(): return QCircuit.wrap_gate(gate) if not isinstance(gate, PowerGateImpl): return QCircuit.wrap_gate(gate) if len(gate.target) > 1: return compile_controlled_power(gate=compile_multitarget(gate=gate)) power = gate.power target = gate.target control = gate.control result = QCircuit() result += Phase(target=control[0], control=control[1:], phi=power * pi / 2) result += change_basis(target=target, name=gate.name) result += Rz(target=target, control=control, angle=power * pi) result += change_basis(target=target, name=gate.name, daggered=True) result.n_qubits = result.max_qubit() + 1 return result
def __init__(self, abstract_circuit: QCircuit, variables, noise=None, device=None, qubit_map=None, optimize_circuit=True, *args, **kwargs): """ Parameters ---------- abstract_circuit: QCircuit: the circuit which is to be rendered in the backend language. variables: values for the variables of abstract_circuit noise: optional: noise to apply to abstract circuit. device: optional: device on which to sample (or emulate sampling) abstract circuit. qubit_map: dictionary: a qubit map which maps the abstract qubits in the abstract_circuit to the qubits on the backend there is no need to initialize the corresponding backend types the dictionary should simply be {int:int} (preferred) or {int:name} if None the default will map to qubits 0 ... n_qubits -1 in the backend optimize_circuit: bool: whether or not to attempt backend depth optimization. Defaults to true. args kwargs """ self._input_args = { "abstract_circuit": abstract_circuit, "variables": variables, "noise": noise, "qubit_map": qubit_map, "optimize_circuits": optimize_circuit, "device": device, **kwargs } self.no_translation = False self._variables = tuple(abstract_circuit.extract_variables()) compiler_arguments = self.compiler_arguments if noise is not None: compiler_arguments["cc_max"] = True compiler_arguments["controlled_phase"] = True compiler_arguments["controlled_rotation"] = True compiler_arguments["hadamard_power"] = True # compile the abstract_circuit c = compiler.Compiler(**compiler_arguments) if qubit_map is None: qubit_map = {q: i for i, q in enumerate(abstract_circuit.qubits)} elif not qubit_map == { q: i for i, q in enumerate(abstract_circuit.qubits) }: warnings.warn( "reveived custom qubit_map = {}\n" "This is not fully integrated and might result in unexpected behaviour!" .format(qubit_map), TequilaWarning) if len(qubit_map) > abstract_circuit.max_qubit() + 1: raise TequilaException( "Custom qubit_map has too many qubits {} vs {}".format( len(qubit_map), abstract_circuit.max_qubit() + 1)) if max(qubit_map.keys()) > abstract_circuit.max_qubit(): raise TequilaException( "Custom qubit_map tries to assign qubit {} but we only have {}" .format(max(qubit_map.keys()), abstract_circuit.max_qubit())) # qubit map is initialized to have BackendQubits as values (they carry number and instance attributes) self.qubit_map = self.make_qubit_map(qubit_map) # pre-compilation (still an abstract ciruit, but with gates decomposed depending on backend requirements) compiled = c(abstract_circuit) self.abstract_circuit = compiled self.noise = noise self.check_device(device) self.device = self.retrieve_device(device) # translate into the backend object self.circuit = self.create_circuit(abstract_circuit=compiled, variables=variables) if optimize_circuit and noise is None: self.circuit = self.optimize_circuit(circuit=self.circuit)