0
votes

I want to pass a complex object to a Q# operation from Python code. I defined the same data structure on both sides: in Python as a class and in Q# as a newtype. Than I prepared a JSON representation in Python of the complex object (exploiting the json package and this answer) and tried to pass it to a Q# operation, but I got the following error:

Received invalid parameters. Please fix and try again:
n: Unexpected initial token 'String' when populating object. Expected JSON object or array. Path '', line 1, position 171.

This is the Python code

import qsharp
import json
import inspect

from json.test import JsonTest

class ObjectEncoder(json.JSONEncoder):
    def default(self, obj):
        if hasattr(obj, "to_json"):
            return self.default(obj.to_json())
        elif hasattr(obj, "__dict__"):
            d = dict(
                (key, value)
                for key, value in inspect.getmembers(obj)
                if not key.startswith("__")
                and not inspect.isabstract(value)
                and not inspect.isbuiltin(value)
                and not inspect.isfunction(value)
                and not inspect.isgenerator(value)
                and not inspect.isgeneratorfunction(value)
                and not inspect.ismethod(value)
                and not inspect.ismethoddescriptor(value)
                and not inspect.isroutine(value)
            )
            return self.default(d)
        return obj

class U:
    x = 0
    y = 0
    z = 0
    def __init__(self, x, y, z) :
        self.x = x
        self.y = y
        self.z = z

class N:
    t = [U(0,0,0), U(3.14,0,0)]
    q = 3
    def __init__(self, t, q) :
        self.t = t
        self.q = q

obj = N([U(3.14, 0, 0), U(0, 3.14, 0)], 3)
jsonObj = json.dumps(obj, cls=ObjectEncoder, indent=2, sort_keys=False)
print(jsonObj)
JsonTest.simulate(n=jsonObj)

This is the JSON rtepresentation printed by that code

{
  "q": 3,
  "t": [
    {
      "x": 3.14,
      "y": 0,
      "z": 0
    },
    {
      "x": 0,
      "y": 3.14,
      "z": 0
    }
  ]
}

This is the Q# code

namespace json.test {

    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Intrinsic;

    newtype U = (
        x : Double,
        y : Double,
        z : Double  
    );

    newtype N = (
        t : U[],
        q : Int
    );

    operation JsonTest(n : N) : Int {
        let r = n::q * Length(n::t); // just do sometinhg
        Message($"t = {n::t}  q = {n::q}");
        return r;
    }
}

Does Q# actually support JSON representation of complex objects?

1
I also tried to manually encode the complex object in a more Q#-like fashion ([(3.1, 0.0, 0.0),(0.0, 3.1, 0.0)], 3) but I got always the same errorStefano

1 Answers

3
votes

Yes, Q# supports representing instances of these user-defined types via Python, but not using dicts or JSON strings. To use the Q# interface, we need to use tuples instead. If you want a data structure that contains the labels you are using (x, y, z and so on), I would recommend using namedtuples, for example:

from collections import namedtuple

U = namedtuple("U", ["x", "y", "z"])
N = namedtuple("N", ["t", "q"])

obj = N(q=3, t=[U(x=3.14, y=0, z=0), U(x=0, y=3.14, z=0)])

such that

>>> obj
N(t=[U(x=3.14, y=0, z=0), U(x=0, y=3.14, z=0)], q=3)

You can then simply pass this to your compiled Q# program like so:

import qsharp

qsharp.compile("""
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Intrinsic;

newtype U = (
    x: Double, 
    y: Double, 
    z: Double
);

newtype N = (
    t: U[],
    q: Int
);
""")

JsonTest = qsharp.compile("""
operation JsonTest(n : N) : Int {
    let r = n::q * Length(n::t); // just do sometinhg
    Message($"t = {n::t}  q = {n::q}");
    return r;
}
""")

from collections import namedtuple

U = namedtuple("U", ["x", "y", "z"])
N = namedtuple("N", ["t", "q"])

obj = N(q=3, t=[U(x=3.14, y=0, z=0), U(x=0, y=3.14, z=0)])
JsonTest.simulate(n=obj)

which returns

t = [U((3.14, 0, 0)),U((0, 3.14, 0))]  q = 3
6

If you want to use JSON strings, you can create a custom function like so:

import json

def N_from_json(data: str):
    """Create N object from JSON-formatted string
    """
    _data = json.loads(data)
    t = _data.get("t", [])
    q = _data.get("q", 0) # Or some other default value of your choosing

    return N(t=[U(**_t) for _t in t], q=q)


jsonObj = json.dumps({
  "t": [
    {
      "x": 3.14,
      "y": 0,
      "z": 0
    },
    {
      "x": 0,
      "y": 3.14,
      "z": 0
    }
  ],
  "q": 3,
})

obj = N_from_json(jsonObj)