3

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.

Sam Jaques
  • 291
  • 3
  • 13
  • In C# there are these things called 'delegates'. Every Method is a delegate, (more reading [here](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/), and you can use delegates as parameters allowing you to pass methods as parameters to methods. If you can post the method signature of `QuantumOperation.run()` I could give you a delegate to pass it as parameter – MindSwipe Oct 02 '19 at 11:45
  • How do you find the method signature? – Sam Jaques Oct 02 '19 at 11:46
  • Method signature means name, parameters, return type and access modifier of a method. To create a delegate you need the return type and the parameter. Check out https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/ – Erik T. Oct 02 '19 at 11:50
  • The Method signature of a given method is where it is declared. For example the method signature of the `ToString` method is `public string ToString();` – MindSwipe Oct 02 '19 at 12:04
  • Okay, I figured out the method signature and tried it, but it didn't store the results (I edited the question to include more details on this) – Sam Jaques Oct 02 '19 at 12:04
  • Could you please provide a [mcve]? A clear and concise code snippet that demonstrates your issue would be very helpful. – dymanoid Oct 02 '19 at 12:05

6 Answers6

2

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);
        }
    }
}
MindSwipe
  • 7,193
  • 24
  • 47
2

The way the Q# simulation tests deal with this is by having a method that receives a delegate with some code you want to execute on the simulator, in particular, the simulator unittests have the RunWithMultipleSimulators method that is broadly used in places like CoreTests.cs; this is an example of how it is used:

    [Fact]
    public void RandomOperation()
    {
        Helper.RunWithMultipleSimulators((s) =>
        {
            Circuits.RandomOperationTest.Run(s).Wait(); // Throws if it doesn't succeed
        });
    }
El capi
  • 451
  • 2
  • 3
  • Very interesting but my goal was to give different parameters to the trace simulator (e.g., different depth settings for different gates), and then also to call the same operation with an integer input for the number of qubits. – Sam Jaques Oct 03 '19 at 13:01
2

I think you're having two separate problems: you're not getting the results back, and dealing with classes is making looping through different operations difficult. Let me try to address them separately.

Results from running an operation are returned from the Run method, not stored in the simulator. More specifically, if you invoke an operation that returns a Q# int, the return value of the Run method will be Task<long>. You can then use the value property of the task to get the actual result, or use the async/await pattern, whichever you like.

All of the operation classes can be instantiated, and they all implement the ICallable interface. This interface has an Apply method that gets passed the arguments to the operation and returns the (asynchronous) results. Each instance has to get properly instantiated with a reference to the simulator; the easiest way to do this is to call the Get generic method on the simulator instance.

If you look at SimulatorBase.cs, in the implementation of the Run method on line 101, you can see how this is done. In this method, T is the class of the operation; I is the class of the operation input; and O is the class of the operation return value. You could use basically the same code to create a list of objects that you then call Apply on with varying arguments.

Alan Geller
  • 494
  • 2
  • 5
  • Thanks for the explanation! Yes it turns out there was a separate issue stopping the results. I think they *are* stored in the simulator object. I'm interested in the simulator's resource estimates, not the output of the quantum operations. Unfortunately I don't understand what the `Run` method is doing. Luckily, using delegates is working for now so hopefully I can get by with that. – Sam Jaques Oct 03 '19 at 13:07
1

I did not understand everything but from the little that I understood you can use a non static wrapper and each wrapper allows accessing to a distinct Qop static class.

static public void TestQop()
{
  someMethod(new Qop1(), 0, 0, 0);
  someMethod(new Qop2(), 1, 1, 1);
}

static void someMethod<T>(T qop, int simulator, int param1, int param2) 
  where T : QopBase
{
  qop.Run(simulator, param1, param2);
}

abstract class QopBase
{
  public abstract void Run(int simulator, int param1, int param2);
}

class Qop1 : QopBase
{
  public override void Run(int simulator, int param1, int param2)
  {
    QuantumOperation1.Run(simulator, param1, param2);
  }
}

class Qop2 : QopBase
{
  public override void Run(int simulator, int param1, int param2)
  {
    QuantumOperation2.Run(simulator, param1, param2);
  }
}
1

Calling a method on an object whose type is generically defined requires you to use a generic constraint which ensures that the used generic type defines the expected method.

At its core, this relies on polymorphism to ensure that even though the specific type can vary, it is known that all usable generic types (which can be limited via constraints) contain this specific method you wish to call.

Static classes and methods lack this feature. They cannot inherit, nor can they implement interfaces, nor can you pass them via method parameters (and trying to do it via generic is not the solution). There is no way to create an "inheritance-like" link between two static methods of two different static classes; even if the methods have the same signature otherwise.

Are there other ways? Yes. In order of preferability:

(1) The straightforward and clean solution is avoiding statics and instead use instanced classes. If you are able to do this, this is the superior option.

(2) If you can't avoid statics, you can still wrap your static in an instanced wrapper, e.g.:

public class IWrapper
{
    void DoTheThing(int foo);
}

public QuantumOperationWrapper : IWrapper
{
    public void DoTheThing(int foo)
    {
        QuantumOperationWrapper.Run(foo);
    }
}

public OtherStaticOperationWrapper : IWrapper
{
    public void DoTheThing(int foo)
    {
        OtherStaticOperationWrapper.Run(foo);
    }
}

This effectively "unstatics" the static code, in a way that you can now rely on the knowledge that all your wrappers implement/inherit the common BaseWrapper and thus both implement the DoTheThing method.

Your generic method can then rely on this:

public void DoTheGenericThing<T>(T obj) where T : IWrapper
{
    obj.DoTheThing(123);
}

Note: In this particular case you don't even need generics to begin with. I assume you don't really need generics in this case, but since the answer can apply to both generic and non-generic cases, I've left the generic parameter in the solution. There may be specific cases in which you still need to use generics, though I suspect this is not one of them.

(3) A third but very dirty option is to use reflection to call the method anyway and just assume you never pass in a type which does not have the expected static method. But this is a really bad practice approach which will be fraught with bugs, it will be nigh impossible to debug, and it's absolutely not refactor-friendly.

Flater
  • 12,908
  • 4
  • 39
  • 62
0

Maybe you can try to deal with the situation using Interfaces. Something like that:

public interface IQuantumOperation 
{
    void Run();
    void Run(MyFancyClazz simulator, MyFancyParam  param1, MyFancyParam param2);
    //And other possible methods
}

Then you can make use of this Interface as a type parameter's contract

static void someMethod<Qop>(Qop myQopParameter) where Qop : IQuantumOperation 
{
    ...
    //Now you can call your Run method
    myQopParameter.Run(...);
    ...
    //Or other fancy Run method with parameters like below
    myQopParameter.Run(simulator, param1, param2);
}

Finally make sure that your QuantumOperation class implements the IQuantumOperation interface

MindSwipe
  • 7,193
  • 24
  • 47
ali kerim erkan
  • 350
  • 2
  • 3
  • 16
  • Quotes are meant for when you are directly quoting something or someone, please use regular text for your answer body – MindSwipe Oct 02 '19 at 12:27