Skip to content

Release 0.20.0

Compare
Choose a tag to compare
@thisac thisac released this 19 Nov 02:03
· 50 commits to master since this release
52e55d6

New features since last release

  • The generic multimode Gaussian gate Ggate is now available in the sf.ops module with the backend choice of tf. The N mode Ggate can be parametrized by a real symplectic matrix S (size 2N * 2N) and a diplacement vector d (size N). You can also obtain the gradients of the Ggate gate via TensorFlow's tape.gradient (#599) (#606)

    from thewalrus.random import random_symplectic
    
    num_mode = 2
    cutoff = 10
    S = tf.Variable(random_symplectic(num_mode))
    d = tf.Variable(np.random.random(2 * num_mode))
    
    eng = sf.Engine("tf", backend_options={"cutoff_dim": cutoff})
    prog = sf.Program(2)
    
    with prog.context as q:
        sf.ops.Ggate(S, d) | (q[0], q[1])
    
    state_out = eng.run(prog).state.ket()

    Note that in order to update the parameter S by using its gradient, you cannot use gradient descent directly (as the unitary would not be symplectic after the update). Please use the function sf.backends.tfbackend.update_symplectic which is designed specifically for this purpose.

    def overlap_loss(state, objective):
        return -tf.abs(tf.reduce_sum(tf.math.conj(state) * objective)) ** 2
    
    def norm_loss(state):
        return -tf.abs(tf.linalg.norm(state)) ** 2
    
    def loss(state, objective):
        return overlap_loss(state, objective) + norm_loss(state)
    
    num_mode = 1
    cutoff = 10
    
    S = tf.Variable(random_symplectic(num_mode))
    d = tf.Variable(np.random.random(2 * num_mode))
    kappa = tf.Variable(0.3)
    objective = tf.Variable(np.eye(cutoff)[1], dtype=tf.complex64)
    
    adam = tf.keras.optimizers.Adam(learning_rate=0.01)
    eng = sf.Engine("tf", backend_options={"cutoff_dim": cutoff})
    prog = sf.Program(1)
    
    with prog.context as q:
        sf.ops.Ggate(S, d) | q
        sf.ops.Kgate(kappa) | q
    
    loss_vals = []
    for _ in range(200):
        with tf.GradientTape() as tape:
            state_out = eng.run(prog).state.ket()
            loss_val = loss(state_out, objective)
    
        eng.reset()
        grad_S, gradients_d, gradients_kappa = tape.gradient(loss_val, [S, d, kappa])
        adam.apply_gradients(zip([gradients_d, gradients_kappa], [d, kappa]))
        update_symplectic(S, grad_S, lr=0.1)  # update S here
        loss_vals.append(loss_val)

Breaking Changes

  • Complex parameters of the Catstate operation are expected in polar form as two separate real parameters. (#441)

  • The sf CLI has been removed in favour of the Xanadu Cloud Client. (#642)

    1. Configuring account credentials using:

      • Strawberry Fields v0.19.0

        $ sf configure --token "foo"
      • Strawberry Fields v0.20.0

        $ xcc config set REFRESH_TOKEN "foo"
        Successfully updated REFRESH_TOKEN setting to 'foo'.
    2. Verifying your connection to the Xanadu Cloud using:

      • Strawberry Fields v0.19.0

        $ sf --ping
        You have successfully authenticated to the platform!
      • Strawberry Fields v0.20.0

        $ xcc ping
        Successfully connected to the Xanadu Cloud.
    3. Submitting a Blackbird circuit to the Xanadu Cloud using:

      • Strawberry Fields v0.19.0

        $ # Version 0.19.0
        $ sf run "foo.xbb"
        Executing program on remote hardware...
        2021-11-02 03:04:05,06 - INFO - The device spec X8_01 has been successfully retrieved.
        2021-11-02 03:04:05,07 - INFO - Compiling program for device X8_01 using compiler Xunitary.
        2021-11-02 03:04:05,08 - INFO - Job b185a63c-f302-4adb-acf8-b6e4e413c11d was successfully submitted.
        2021-11-02 03:04:05,09 - INFO - The remote job b185a63c-f302-4adb-acf8-b6e4e413c11d has been completed.
        [[0 0 0 0]
        [0 0 0 0]
        [0 0 0 0]
        [0 0 0 0]]
      • Strawberry Fields v0.20.0

        $ xcc job submit --name "bar" --target "X8_01" --circuit "$(cat foo.xbb)"
        {
            "id": "0b0f5a46-46d8-4157-8005-45a4764361ba",  # Use this ID below.
            "name": "bar",
            "status": "open",
            "target": "X8_01",
            "language": "blackbird:1.0",
            "created_at": "2021-11-02 03:04:05,10",
            "finished_at": null,
            "running_time": null,
            "metadata": {}
        }
        $ xcc job get 0b0f5a46-46d8-4157-8005-45a4764361ba --result
        {
            "output": [
                "[[0 0 0 0]\n[0 0 0 0]\n[0 0 0 0]\n[0 0 0 0]]"
            ]
        }
  • The sf.api.Connection class has been replaced with the xcc.Connection class. (#645)

    Previously, in Strawberry Fields v0.19.0, an sf.RemoteEngine can be instantiated with a custom Xanadu Cloud connection as follows:

    import strawberryfields as sf
    import strawberryfields.api
    
    connection = strawberryfields.api.Connection(
      token="Xanadu Cloud API key goes here",
      host="platform.strawberryfields.ai",
      port=443,
      use_ssl=True,
    )
    engine = sf.RemoteEngine("X8", connection=connection)

    In Strawberry Fields v0.20.0, the same result can be achieved using

    import strawberryfields as sf
    import xcc
    
    connection = xcc.Connection(
      refresh_token="Xanadu Cloud API key goes here",  # See "token" argument above.
      host="platform.strawberryfields.ai",
      port=443,
      tls=True,                                        # See "use_ssl" argument above.
    )
    engine = sf.RemoteEngine("X8", connection=connection)
  • The sf.configuration module has been replaced with the xcc.Settings class. (#649)

    This means that Xanadu Cloud credentials are now stored in exactly one location, the path to which depends on your operating system:

    1. Windows: C:\Users\%USERNAME%\AppData\Local\Xanadu\xanadu-cloud\.env

    2. MacOS: /home/$USER/Library/Application\ Support/xanadu-cloud/.env

    3. Linux: /home/$USER/.config/xanadu-cloud/.env

    The format of the configuration file has also changed to .env and the names of some fields have been updated. For example,

    # Strawberry Fields v0.19.0 (config.toml)
    [api]
    authentication_token = "Xanadu Cloud API key goes here"
    hostname = "platform.strawberryfields.ai"
    port = 443
    use_ssl = true

    is equivalent to

    # Strawberry Fields v0.20.0 (.env)
    XANADU_CLOUD_REFRESH_TOKEN='Xanadu Cloud API key goes here'
    XANADU_CLOUD_HOST='platform.strawberryfields.ai'
    XANADU_CLOUD_PORT=443
    XANADU_CLOUD_TLS=True

    Similarly, the names of the configuration environment variables have changed from

    # Strawberry Fields v0.19.0
    export SF_API_AUTHENTICATION_TOKEN="Xanadu Cloud API key goes here"
    export SF_API_HOSTNAME="platform.strawberryfields.ai"
    export SF_API_PORT=443
    export SF_API_USE_SSL=true

    to

    # Strawberry Fields v0.20.0
    export XANADU_CLOUD_REFRESH_TOKEN="Xanadu Cloud API key goes here"
    export XANADU_CLOUD_HOST="platform.strawberryfields.ai"
    export XANADU_CLOUD_PORT=443
    export XANADU_CLOUD_TLS=true

    Finally, strawberryfields.store_account() has been replaced such that

    # Strawberry Fields v0.19.0
    import strawberryfields as sf
    sf.store_account("Xanadu Cloud API key goes here")

    becomes

    # Strawberry Fields v0.20.0
    import xcc
    xcc.Settings(REFRESH_TOKEN="Xanadu Cloud API key goes here").save()
  • The sf.api.Job class has been replaced with the xcc.Job class. (#650)

    A Job object is returned when running jobs asynchronously. In previous versions of Strawberry Fields (v0.19.0 and lower), the Job object can be used as follows:

    >>> job = engine.run_async(program, shots=1)
    >>> job.status
    'queued'
    >>> job.result
    InvalidJobOperationError
    >>> job.refresh()
    >>> job.status
    'complete'
    >>> job.result
    [[0 1 0 2 1 0 0 0]]

    In Strawberry Fields v0.20.0, the Job object works slightly differently:

    >>> job = engine.run_async(program, shots=1)
    >>> job.status
    'queued'
    >>> job.wait()
    >>> job.status
    'complete'
    >>> job.result
    {'output': [array([[0 1 0 2 1 0 0 0]])]}

    The job.wait() method is a blocking method that will wait for the job to finish. Alternatively, job.clear() can be called to clear the cache, allowing job.status to re-fetch the job status.

  • The sf.api.Result class has been updated to support the Xanadu Cloud Client integration. (#651)

    While Result.samples should return the same type and shape as before, the Result.all_samples property has been renamed to Result.samples_dict and returns the samples as a dictionary with corresponding measured modes as keys.

    >>> res = eng.run(prog, shots=3)
    >>> res.samples
    array([[1, 0], [0, 1], [1, 1]])
    >>> res.samples_dict
    {0: [np.array([1, 0, 1])], 1: [np.array([0, 1, 1])]}

    The samples dictionary is only accessible for simulators.

  • The sf.api.DeviceSpec class has been updated to support the Xanadu Cloud Client integration. (#644)

    It now works as a container for a device specification dictionary. There are no more API connection usages, and DeviceSpec.target is retrieved from the device specification rather than passed at initialization.

  • The api subpackage has been removed and the contained DeviceSpec and Result classes have been moved to the root strawberryfields folder. (#652)

    They can now be imported as follows:

    import strawberryfields  as sf
    # sf.DeviceSpec
    # sf.Result

Documentation

  • Strawberry Fields interactive has been removed from the documentation. (#635)

Contributors

This release contains contributions from (in alphabetical order):

Mikhail Andrenkov, Sebastián Duque Mesa, Theodor Isacsson, Josh Izaac, Filippo Miatto, Nicolás Quesada, Antal Száva, Yuan Yao.