def projectq_expectation_value(circuit: Circuit,
                               hamiltonian: QubitOperator) -> float:
    ProjectQback = Simulator()
    fwd = ForwarderEngine(ProjectQback)
    eng = MainEngine(backend=ProjectQback, engine_list=[fwd])
    qureg = eng.allocate_qureg(circuit.n_qubits)
    tk_to_projectq(eng, qureg, circuit)
    eng.flush()
    energy = eng.backend.get_expectation_value(hamiltonian, qureg)
    All(Measure) | qureg
    return energy
Exemple #2
0
 def get_state(self,circuit:Circuit, fit_to_constraints=True) -> list:
     c = circuit.copy()
     if fit_to_constraints :
         Transform.RebaseToProjectQ().apply(c)
     fwd = ForwarderEngine(self._backend)
     eng = MainEngine(backend=self._backend,engine_list=[fwd])
     qureg = eng.allocate_qureg(c.n_qubits)
     tk_to_projectq(eng,qureg,c)
     eng.flush()
     state = self._backend.cheat()[1] #`cheat()` returns tuple:(a dictionary of qubit indices, statevector)
     All(Measure) | qureg
     return state #list of complex numbers
    def process_circuits(
        self,
        circuits: Iterable[Circuit],
        n_shots: Optional[int] = None,
        valid_check: bool = True,
        **kwargs: KwargTypes,
    ) -> List[ResultHandle]:
        """
        See :py:meth:`pytket.backends.Backend.process_circuits`.
        Supported kwargs: `seed`.
        """
        circuit_list = list(circuits)
        if valid_check:
            self._check_all_circuits(circuit_list)

        handle_list = []
        for circuit in circuit_list:
            sim = Simulator(rnd_seed=kwargs.get("seed"))
            fwd = ForwarderEngine(sim)
            eng = MainEngine(backend=sim, engine_list=[fwd])
            qureg = eng.allocate_qureg(circuit.n_qubits)
            tk_to_projectq(eng, qureg, circuit, True)
            eng.flush()
            state = np.array(
                eng.backend.cheat()[1], dtype=complex
            )  # `cheat()` returns tuple:(a dictionary of qubit indices, statevector)
            handle = ResultHandle(str(uuid4()))
            try:
                phase = float(circuit.phase)
                coeff = np.exp(phase * np.pi * 1j)
                state *= coeff
            except ValueError:
                warning(
                    "Global phase is dependent on a symbolic parameter, so cannot "
                    "adjust for phase")
            implicit_perm = circuit.implicit_qubit_permutation()
            # reverse qubits as projectq state is dlo
            res_qubits = [
                implicit_perm[qb]
                for qb in sorted(circuit.qubits, reverse=True)
            ]
            measures = circuit.n_gates_of_type(OpType.Measure)
            if measures == 0 and n_shots is not None:
                backres = self.empty_result(circuit, n_shots=n_shots)
            else:
                backres = BackendResult(q_bits=res_qubits, state=state)
            self._cache[handle] = {"result": backres}
            handle_list.append(handle)
        return handle_list
Exemple #4
0
    def run(self, circuit:Circuit, shots:int, fit_to_constraints=True) -> np.ndarray:
        state = self.get_state(circuit, fit_to_constraints)
        fwd = ForwarderEngine(self._backend)
        eng = MainEngine(backend=self._backend,engine_list=[fwd])
        qb_results = []
        qureg = eng.allocate_qureg(circuit.n_qubits)

        for _ in range(shots):
            self._backend.set_wavefunction(state,qureg)
            All(Measure) | qureg
            eng.flush()
            results = (list(map(int,qureg)))
            qb_results.append(results)
        
        return np.asarray(qb_results)
 def _expectation_value(
     self,
     circuit: Circuit,
     hamiltonian: projectq.ops.QubitOperator,
     valid_check: bool = True,
 ) -> complex:
     if valid_check and not self.valid_circuit(circuit):
         raise ValueError(
             "Circuits do not satisfy all required predicates for this backend"
         )
     sim = Simulator()
     fwd = ForwarderEngine(sim)
     eng = MainEngine(backend=sim, engine_list=[fwd])
     qureg = eng.allocate_qureg(circuit.n_qubits)
     tk_to_projectq(eng, qureg, circuit)
     eng.flush()
     energy = eng.backend.get_expectation_value(hamiltonian, qureg)
     return complex(energy)
    def _send_cnot(self, cmd, control, target, flip=False):
        def cmd_mod(command):
            command.tags = deepcopy(cmd.tags) + command.tags
            command.engine = self.main_engine
            return command

        # We'll have to add all meta tags before sending on
        cmd_mod_eng = CommandModifier(cmd_mod)
        cmd_mod_eng.next_engine = self.next_engine
        cmd_mod_eng.main_engine = self.main_engine
        # forward everything to the command modifier
        forwarder_eng = ForwarderEngine(cmd_mod_eng)
        target[0].engine = forwarder_eng
        control[0].engine = forwarder_eng
        if flip:
            # flip the CNOT using Hadamard gates:
            All(H) | (control + target)
            CNOT | (target, control)
            All(H) | (control + target)
        else:
            CNOT | (control, target)
Exemple #7
0
    def _run(self):
        """
        Runs all stored gates.

        Raises:
            Exception:
                If the mapping to the IBM backend cannot be performed or if
                the mapping was already determined but more CNOTs get sent
                down the pipeline.
        """
        mapping = []
        if len(self._interactions) > 0:
            ids_and_interactions = []
            qubits_to_map = set()
            for qubit_id, interactions in self._interactions.items():
                if len(interactions) > 2:
                    num_interactions = len(interactions)
                else:
                    num_interactions = 2
                ids_and_interactions += [[
                    qubit_id, num_interactions, self._num_cnot_target[qubit_id]
                ]]
                qubits_to_map.add(qubit_id)
            # sort by #interactions (and #times it is the target qubit
            # for optimization purposes):
            ids_and_interactions.sort(key=lambda t: (-t[1], -t[2]))
            mapped_id = ids_and_interactions[0][0]
            corrected_counts = deepcopy(self._num_cnot_target)
            # correct CNOT target counts (the first ID is now fixed):
            for cmd in self._cmds:
                if (self._is_cnot(cmd)
                        and cmd.control_qubits[0].id == mapped_id):
                    corrected_counts[cmd.qubits[0][0].id] -= 1
            # update interaction counts
            for i in range(1, len(ids_and_interactions)):
                ids_and_interactions[i][-1] = (
                    corrected_counts[ids_and_interactions[i][0]])
            ids_and_interactions.sort(key=lambda t: (-t[1], -t[2]))
            # map remaining interactions to connectivity graph
            # where necessary. Else: map according to # of times the qubit
            # is a target qubit in a CNOT.
            interactions = deepcopy(self._interactions)
            places = list(range(5))
            i = 0
            place_idx = 0
            mapping = []
            while len(qubits_to_map) > 0:
                last_mapped_id = ids_and_interactions[i][0]
                ids_and_interactions = (ids_and_interactions[0:i] +
                                        ids_and_interactions[i + 1:])
                current_place = places[place_idx]
                mapping.append((last_mapped_id, current_place))
                qubits_to_map.remove(last_mapped_id)
                places = places[0:place_idx] + places[place_idx + 1:]
                if not len(places) == 4:  # currently mapping non-center qubit
                    # map (potential) interaction partner
                    num_partners = len(interactions[last_mapped_id])
                    if num_partners > 1:
                        self._reset()
                        raise Exception("Mapping without SWAPs failed! "
                                        "Sorry...")
                    elif num_partners == 1:
                        partner_id = interactions[last_mapped_id].pop()
                        idx = [
                            j for j in range(len(ids_and_interactions))
                            if ids_and_interactions[j][0] == partner_id
                        ]
                        i = idx[0]
                        place_idx = [
                            pidx for pidx in range(len(places))
                            if places[pidx] == current_place + 2
                        ][0]
                    else:
                        i = 0
                        place_idx = 0
                for idx in interactions:
                    if last_mapped_id in interactions[idx]:
                        interactions[idx].remove(last_mapped_id)

            self._interactions = dict()

        target_indices = {mp[0]: mp[1] for mp in mapping if mp[1] <= 2}
        all_indices = {mp[0]: mp[1] for mp in mapping}
        for cmd in self._cmds:
            if self._needs_flipping(cmd, all_indices):
                # To have nicer syntax when flipping CNOTs, we'll use a
                # forwarder engine and a command modifier to get the tags
                # right. (If the CNOT is an 'uncompute', then so must be the
                # remapped CNOT)
                def cmd_mod(command):
                    command.tags = cmd.tags[:] + command.tags
                    command.engine = self.main_engine
                    return command

                # We'll have to add all meta tags before sending on
                cmd_mod_eng = CommandModifier(cmd_mod)
                cmd_mod_eng.next_engine = self.next_engine
                cmd_mod_eng.main_engine = self.main_engine
                # forward everything to the command modifier
                forwarder_eng = ForwarderEngine(cmd_mod_eng)
                cmd.engine = forwarder_eng
                qubit = cmd.qubits[0]
                ctrl = cmd.control_qubits
                # flip the CNOT using Hadamard gates:
                All(H) | (ctrl + qubit)
                CNOT | (qubit, ctrl)
                All(H) | (ctrl + qubit)
            elif cmd.gate == Allocate:
                ibm_order = [2, 1, 4, 0, 3]
                cmd.tags += [
                    QubitPlacementTag(
                        ibm_order[all_indices[cmd.qubits[0][0].id]])
                ]
                self.next_engine.receive([cmd])
            else:
                self.next_engine.receive([cmd])

        self._cmds = []
Exemple #8
0
    def _process_command(self, cmd):
        """
        Check whether a command cmd can be handled by further engines and,
        if not, replace it using the decomposition rules loaded with the setup
        (e.g., setups.default).

        Args:
            cmd (Command): Command to process.

        Raises:
            Exception if no replacement is available in the loaded setup.
        """
        if self.is_available(cmd):
            self.send([cmd])
        else:
            # check for decomposition rules
            decomp_list = []
            potential_decomps = []

            # First check for a decomposition rules of the gate class, then
            # the gate class of the inverse gate. If nothing is found, do the
            # same for the first parent class, etc.
            gate_mro = type(cmd.gate).mro()[:-1]
            # If gate does not have an inverse it's parent classes are
            # DaggeredGate, BasicGate, object. Hence don't check the last two
            inverse_mro = type(get_inverse(cmd.gate)).mro()[:-2]
            rules = self.decompositionRuleSet.decompositions
            for level in range(max(len(gate_mro), len(inverse_mro))):
                # Check for forward rules
                if level < len(gate_mro):
                    class_name = gate_mro[level].__name__
                    try:
                        potential_decomps = [d for d in rules[class_name]]
                    except KeyError:
                        pass
                    # throw out the ones which don't recognize the command
                    for d in potential_decomps:
                        if d.check(cmd):
                            decomp_list.append(d)
                    if len(decomp_list) != 0:
                        break
                # Check for rules implementing the inverse gate
                # and run them in reverse
                if level < len(inverse_mro):
                    inv_class_name = inverse_mro[level].__name__
                    try:
                        potential_decomps += [
                            d.get_inverse_decomposition()
                            for d in rules[inv_class_name]
                        ]
                    except KeyError:
                        pass
                    # throw out the ones which don't recognize the command
                    for d in potential_decomps:
                        if d.check(cmd):
                            decomp_list.append(d)
                    if len(decomp_list) != 0:
                        break

            if len(decomp_list) == 0:
                raise NoGateDecompositionError("\nNo replacement found for " +
                                               str(cmd) + "!")

            # use decomposition chooser to determine the best decomposition
            chosen_decomp = self._decomp_chooser(cmd, decomp_list)

            # the decomposed command must have the same tags
            # (plus the ones it gets from meta-statements inside the
            # decomposition rule).
            # --> use a CommandModifier with a ForwarderEngine to achieve this.
            old_tags = cmd.tags[:]

            def cmd_mod_fun(cmd):  # Adds the tags
                cmd.tags = old_tags[:] + cmd.tags
                cmd.engine = self.main_engine
                return cmd

            # the CommandModifier calls cmd_mod_fun for each command
            # --> commands get the right tags.
            cmod_eng = CommandModifier(cmd_mod_fun)
            cmod_eng.next_engine = self  # send modified commands back here
            cmod_eng.main_engine = self.main_engine
            # forward everything to cmod_eng using the ForwarderEngine
            # which behaves just like MainEngine
            # (--> meta functions still work)
            forwarder_eng = ForwarderEngine(cmod_eng)
            cmd.engine = forwarder_eng  # send gates directly to forwarder
            # (and not to main engine, which would screw up the ordering).

            chosen_decomp.decompose(cmd)  # run the decomposition
Exemple #9
0
    def _run(self):
        """
		Runs all stored gates.

		Raises:
			Exception:
				If the mapping to the IBM backend cannot be performed or if the
				mapping was already determined but more CNOTs get sent down the
				pipeline.
		"""
        cnot_id = self._cnot_id
        if cnot_id == -1 and len(self._cnot_ids) > 0:
            cnot_id = self._cnot_ids[0]
            if len(self._cnot_ids
                   ) == 2:  # we can optimize (at least a little bit)!
                count1 = 0
                count2 = 0
                for cmd in self._cmds:
                    if self._is_cnot(cmd):
                        qb = cmd.qubits[0][0]
                        ctrl = cmd.control_qubits[0]
                        if ctrl.id == self._cnot_ids[0]:
                            count1 += 1
                        elif ctrl.id == self._cnot_ids[1]:
                            count2 += 1
                if count1 > count2:
                    cnot_id = self._cnot_ids[1]

        for cmd in self._cmds:
            if self._is_cnot(cmd) and not cmd.qubits[0][0].id == cnot_id:
                # we have to flip it around. To have nice syntax, we'll use a
                # forwarder engine and a command modifier to get the tags right.
                # (If the CNOT is an 'uncompute', then so must be the remapped CNOT)
                def cmd_mod(command):
                    command.tags = cmd.tags[:] + command.tags
                    command.engine = self.main_engine
                    return command

                cmd_mod_eng = CommandModifier(
                    cmd_mod)  # will add potential meta tags
                cmd_mod_eng.next_engine = self.next_engine  # and send on all commands
                cmd_mod_eng.main_engine = self.main_engine
                # forward everything to the command modifier
                forwarder_eng = ForwarderEngine(cmd_mod_eng)
                cmd.engine = forwarder_eng  # and send all gates to forwarder engine.

                qubit = cmd.qubits[0]
                ctrl = cmd.control_qubits
                # flip the CNOT using Hadamard gates:
                All(H) | (ctrl + qubit)
                CNOT | (qubit, ctrl)
                All(H) | (ctrl + qubit)

                # This cmd would require remapping -->
                # raise an exception if the CNOT id has already been determined.
                if self._cnot_id != -1:
                    self._reset()
                    raise Exception(
                        "\nIBM Quantum Experience does not allow "
                        "intermediate measurements / \ndestruction of "
                        "qubits! CNOT mapping may be inconsistent.\n")
            else:
                self.next_engine.receive([cmd])
        self._cmds = []
        self._cnot_id = cnot_id
Exemple #10
0
    def _process_command(self, cmd):
        """
        Check whether a command cmd can be handled by further engines and,
        if not, replace it using the decomposition rules loaded with the setup
        (e.g., setups.default).

        Args:
            cmd (Command): Command to process.

        Raises:
            Exception if no replacement is available in the loaded setup.
        """
        if self.is_available(cmd):
            self.send([cmd])
        else:
            # check for decomposition rules
            decomp_list = []
            potential_decomps = []
            inv_list = []

            # check for forward rules
            cls = cmd.gate.__class__.__name__
            try:
                potential_decomps = [
                    d for d in self.decompositionRuleSet.decompositions[cls]
                ]
            except KeyError:
                pass
            # check for rules implementing the inverse gate
            # and run them in reverse
            inv_cls = get_inverse(cmd.gate).__class__.__name__
            try:
                potential_decomps += [
                    d.get_inverse_decomposition()
                    for d in self.decompositionRuleSet.decompositions[inv_cls]
                ]
            except KeyError:
                pass
            # throw out the ones which don't recognize the command
            for d in potential_decomps:
                if d.check(cmd):
                    decomp_list.append(d)

            if len(decomp_list) == 0:
                raise NoGateDecompositionError("\nNo replacement found for "
                                               + str(cmd) + "!")

            # use decomposition chooser to determine the best decomposition
            chosen_decomp = self._decomp_chooser(cmd, decomp_list)

            # the decomposed command must have the same tags
            # (plus the ones it gets from meta-statements inside the
            # decomposition rule).
            # --> use a CommandModifier with a ForwarderEngine to achieve this.
            old_tags = cmd.tags[:]

            def cmd_mod_fun(cmd):  # Adds the tags
                cmd.tags = old_tags[:] + cmd.tags
                cmd.engine = self.main_engine
                return cmd
            # the CommandModifier calls cmd_mod_fun for each command
            # --> commands get the right tags.
            cmod_eng = CommandModifier(cmd_mod_fun)
            cmod_eng.next_engine = self  # send modified commands back here
            cmod_eng.main_engine = self.main_engine
            # forward everything to cmod_eng using the ForwarderEngine
            # which behaves just like MainEngine
            # (--> meta functions still work)
            forwarder_eng = ForwarderEngine(cmod_eng)
            cmd.engine = forwarder_eng  # send gates directly to forwarder
            # (and not to main engine, which would screw up the ordering).

            chosen_decomp.decompose(cmd)  # run the decomposition