3
$\begingroup$

I want to write code that prints out the controls of each operation executed during a simulation in Q#. For example this code prints the control counts:

var qsim = new QCTraceSimulator(config);
qsim.OnOperationStart += (op, arg) => {
    Console.WriteLine($"{Controls(op, arg).Length"});
}

I'm having trouble writing the Controls function, which extracts a list of qubits being used as controls. When the operation is uncontrolled, or controlled by 0 qubits, the returned array should be of length 0.

The issue I'm running into is that the type and layout of arg.Value varies from operation to operation, even after conditioning on op.Variant being OperationFunctor.ControlledAdjoint or OperationFunctor.Controlled. I can handle individual cases by inspecting the types, but I keep running into new unhandled cases. This indicates there's probably a "correct" way to do this that I'm missing.

In short, how do I implement this function:

object[] Controls(ICallable op, IApplyData arg) {
    ???
}

By "controls" I always mean the cs in Controlled Op(cs, ...). The same operation may have different controls when expressed in different ways. For example, the controls list of Controlled Toffoli(a, (b, c, d)) is the list [a] whereas the controls list of Controlled X([a, b, c], d) is the list [a, b, c]. A further example: the controls list of Toffoli(b, c, d) is [], even though normally one might think of the first two arguments as the controls. It is of course expected that within Toffoli(b, c, d) there may be a sub-operation Controlled X((b, c), d) where the controls list is [b, c]; I'm not thinking of controls as some kind of absolute concept that is invariant as you go down through layers of abstraction.

$\endgroup$
6
$\begingroup$

arg.Value contains the actual tuple that the controlled operation receives at runtime. It's a two item tuple in which the first item is the control qubits, and the second another tuple with the arguments the operation normally expects, so in your case you are only interested in the first item of this tuple.

Overall, arg.Value can be anything, thus it has object as type, but fear not, using a little bit of C#'s reflection is easy to retrieve its content. The implementation you are looking for is this:

    static Qubit[] Controls(ICallable op, IApplyData arg)
    {
        // Uncontrolled operations have no control qubits.
        if (op.Variant != OperationFunctor.Controlled &&
            op.Variant != OperationFunctor.ControlledAdjoint)
        {
            return new Qubit[0];
        }

        // Get the first item of the (controls, args) tuple.
        dynamic v = arg.Value;
        QArray<Qubit> ctrls = v.Item1;
        return ctrls.ToArray();
    }

Notice the array of Qubits is encapsulated in something called a QArray<Qubit>, QArray is the data structure we use in simulation for all Q# arrays.

$\endgroup$
  • $\begingroup$ You can shorten the code using dynamic: dynamic v = arg.Value; QArray<Qubit> ctrls = v.Item1;. $\endgroup$ – Craig Gidney Jan 10 at 10:55
  • $\begingroup$ It may be relevant that I'm using OnOperationEnd instead of OnOperationStart... the arg value seems to change between the two? $\endgroup$ – Craig Gidney Jan 10 at 11:08
  • $\begingroup$ Hey Craig. Yes, the problem is that you are using OnOperationEnd. The value of arg in this case is not the input tuple, but the return tuple (QVoid in your case). If you want to get the list of control qubits, you will have to use OnOperationStart. $\endgroup$ – El capi Jan 11 at 21:01
  • $\begingroup$ I like what you did with dynamic! $\endgroup$ – El capi Jan 11 at 21:02
  • $\begingroup$ I worked around the OnOperationEnd issue by pushing the arg value onto a stack on start and popping during end. $\endgroup$ – Craig Gidney Jan 11 at 21:29

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.