Search code examples
c#typesstatic-classesquantum-computingq#

Using methods from a type passed as a parameter in C#


I'm working in Q#, a quantum programming language based on C#. Quantum operations become C# classes, from which you can do things like

QuantumOperation.run(simulator, param1, param2);

which will use a quantum simulator simulator to run the operation QuantumOperation with the parameters param1 and param2.

I have many different operations which I want to run using different simulators and different parameters. What I would like to do is pass the quantum operation to another method, which will iterate through all the simulators and parameters. Then I can call this method with all the quantum operations I want.

The problem is that - as far as I can tell - a quantum operation is really a class and not an object. So, for example, if I write:

static void someMethod<Qop>(){...}

then I can call this with a quantum operation QuantumOperation as:

someMethod<QuantumOperation>()

and it compiles fine. However, if I try to do something like

static void someMethod<Qop>(Qop quantumOperation){ ...}

someMethod<QuantumOperation>(quantumOperation);

I get an error of "QuantumOperation is a type, which is not valid in the given context" for the second line.

If I try:

static void someMethod<Qop>(...){
    ...
    Qop.Run(...);
    ...
}

it similarly says: "'Qop' is a type parameter, which is not valid in the given context".

What seems to be happening here is that I'm passing the class as a type. But then when I want to treat the type as a class, I can't. I looked for ways to pass a class as an argument, but I only see ways to do this that will create objects in that class. But I can't use an object, since "Run" is a static method.

(I could try passing an object and getting the class from that, but (a) I don't know if it's possible to create objects of quantum operation classes, and (b) I can only find public Type GetType, which returns a type and not a class, giving the same problem).

Is there any way to pass a class as an argument, then reference static methods of that class, without ever instantiating an object?

Now, maybe I'm asking too much, since, as far as C# is concerned, it's a coincidence that all these classes have a method called "Run". It maybe shouldn't be able to attempt to call methods with the same name from different classes.

Alternatively, I could construct a method for each quantum operation and then pass those methods. The method would look like:

static void QuantumOperationWrapper(QuantumSimulator simulator, Int int_parameter){
    QuantumOperation.Run(simulator, in_parameter);
}

I would need to make a new method for each quantum operation, but that's not that bad. Then I can pass this as a delegate or Func to the methods I want. The problem is that the results I want are contained in the QuantumSimulator object. So what I want to do is something like:

QuantumOperationWrapper(simulator, 3);
simulator.GetResults();

But when I do this, the results are empty. My guess is that, somehow, the simulator is being passed by value, or treated as immutable, or something that prevents QuantumOperationWrapper from altering internal parameters of the simulator.

Is there any way to I can ensure that a delegate/Func will alter the internal state of its arguments?

EDIT: I can make a delegate for the Run method, as follows:

public delegate System.Threading.Tasks.Task<Microsoft.Quantum.Simulation.Core.QVoid> RunQop(QCTraceSimulator sim, long n);

Then I can construct static void someMethod(RunQop runner, ...), and pass QuantumOperation.Run as the first argument.

However, I have the same problem, that the QCTraceSimulator I pass as an argument does not keep any of the simulation results it makes when I call this.


Solution

  • So if I understand you correctly you want to execute a bunch of methods with parameters on different simulators. Here is how to do this:

    We first off need a List of the operations we want to perform.

    var methodList = new List<Func<QCTraceSimulator, long, Task<QVoid>>>
    {
        QuantumOperation.Run,
        // Add more methods here
    }
    

    This is a List of Funcs. A Func is a delegate type that represents a method with a parameter and a return value. Here our methods need to look like this to be able to be added to our List:

    public Task<QVoid> SomeName(QCTraceSimulator sim, long parameter)
    { ...}
    

    We also need a list of parameters you want to try this with:

    var paramsList = new List<long>
    {
        1,
        2,
       -2147483648,
        2147483647
    };
    

    Now we can iterate through these and run our method like so:

    public void RunMethodsOnSimulator(QCTraceSimulator sim)
    {
        // Iterate through every method
        foreach (var method in methodList)
        {
            // Iterate through every parameter
            foreach (var parameter in paramsList)
            {
                // Execute the given method with the given parameter
                Task<QVoid> result = method(sim, parameter);
            }
        }
    }
    

    You can now do whatever you want with the result. This will result in every method being called with every parameter once

    Please keep in mind that this answer only solves this problem for methods that return a Task<QVoid> and take a QCTraceSimulator and a long as parameter. This solution however avoids having to modify any QuantumOperation classes (and hopefully teaches you a little about delegates)

    Here is what the paramsList and the RunMethodsOnSimulator method would like with 2 or more parameters:

    methodList = new List<Func<QCTraceSimulator, long, int, Task<QVoid>>>
    {
        QuantumOperation.Run,
        // Add more methods here
    }
    
    paramsList = new List<Tuple<long, int>>
    {
        new Tuple<long, int>(1, 1),
        new Tuple<long, int>(2, 1),
        new Tuple<long, int>(1, 2),
        new Tuple<long, int>(-2147483648, 1)
    }
    
    public void RunMethodsOnSimulator(QCTraceSimulator sim)
    {
        // Iterate through every method
        foreach (var method in methodList)
        {
            // Iterate through every parameter
            foreach (var parameter in paramsList)
            {
                // Execute the given method with the given parameter
                Task<QVoid> result = method(sim, parameter.Item1, parameter.Item2);
            }
        }
    }