Sending some gates

Let's change our plugin to make it do something more useful now. We'll implement the Deutsch–Jozsa algorithm because it's nice and simple. Put briefly, this algorithm determines whether a one-qubit to one-qubit oracle function is constant (x -> 0 or x -> 1) or balanced (x -> x or x -> !x) by only evaluating the function once.

Here's the plugin code:

from dqcsim.plugin import *

@plugin("Deutsch-Jozsa", "Tutorial", "0.1")
class MyPlugin(Frontend):

    def oracle_constant_0(self, qi, qo):
        """x -> 0 oracle function."""
        pass

    def oracle_constant_1(self, qi, qo):
        """x -> 1 oracle function."""
        self.x_gate(qo)

    def oracle_passthrough(self, qi, qo):
        """x -> x oracle function."""
        self.cnot_gate(qi, qo)

    def oracle_invert(self, qi, qo):
        """x -> !x oracle function."""
        self.cnot_gate(qi, qo)
        self.x_gate(qo)

    def deutsch_jozsa(self, qi, qo, oracle):
        """Runs the Deutsch-Jozsa algorithm on the given oracle. The oracle is
        called with the input and output qubits as positional arguments."""

        # Prepare the input qubit.
        self.prepare(qi)
        self.h_gate(qi)

        # Prepare the output qubit.
        self.prepare(qo)
        self.x_gate(qo)
        self.h_gate(qo)

        # Run the oracle function.
        oracle(qi, qo)

        # Measure the input.
        self.h_gate(qi)
        self.measure(qi)
        if self.get_measurement(qi).value:
            self.info('Oracle was balanced!')
        else:
            self.info('Oracle was constant!')

    def handle_run(self):
        qi, qo = self.allocate(2)

        self.info('Running Deutsch-Jozsa on x -> 0...')
        self.deutsch_jozsa(qi, qo, self.oracle_constant_0)

        self.info('Running Deutsch-Jozsa on x -> 1...')
        self.deutsch_jozsa(qi, qo, self.oracle_constant_1)

        self.info('Running Deutsch-Jozsa on x -> x...')
        self.deutsch_jozsa(qi, qo, self.oracle_passthrough)

        self.info('Running Deutsch-Jozsa on x -> !x...')
        self.deutsch_jozsa(qi, qo, self.oracle_invert)

        self.free(qi, qo)

MyPlugin().run()

The plugin runs the algorithm on all possible oracles in sequence. Observe that we hardly had to duplicate any code to do that, by making clever use of Python's expressivity! Also note that DQCsim's Python plugin provides you with a number of built-in gate types. You can find the full list here.

You can run this example with the null backend provided by DQCsim as follows.

$ dqcsim my-plugin.py null
... Info dqcsim  Starting Simulation with seed: 17518393962103239508
... Info back    Running null backend initialization callback
... Info dqcsim  Executing 'start(...)' host call...
... Info dqcsim  Executing 'wait()' host call...
... Info front   Running Deutsch-Jozsa on x -> 0...
... Info front   Oracle was constant!
... Info front   Running Deutsch-Jozsa on x -> 1...
... Info front   Oracle was balanced!
... Info front   Running Deutsch-Jozsa on x -> x...
... Info front   Oracle was balanced!
... Info front   Running Deutsch-Jozsa on x -> !x...
... Info front   Oracle was constant!
... Note dqcsim  'wait()' returned {}
... Info dqcsim  Reproduction file written to "my-plugin.py.repro".
... Info dqcsim  Simulation completed successfully.
... Info dqcsim  PluginProcess exited with status code: 0
... Info dqcsim  PluginProcess exited with status code: 0

Note that the null backend ignores all gates and just returns random measurements, so the algorithm also just returns random results. To make the algorithm work, you'll have to use a real backend.