Juice Identifiers and Run IDs#

Juice provides functionality to attach identifiers to your data. These identifiers can be customized per user-needs. For example, run_IDs, cycle_IDs, system_IDs

We recommend using run IDs to track runs of experiments.

Why use run IDs?

Tagging data with run IDs allows easy trace back of data to a specific run under which it was generated, which is especially useful for organizing and analyzing experimental data.

Juice provides an example implementation for run identifiers. This guide explains how to configure and use run IDs using the identifier configuration.

A run ID is an identifier for tagging data as part of a measurement run. A run ID is typically generated at the start of an measurement session and is used to uniquely identify and track the data associated with that specific run. When a new run ID is generated, it is associated with the service name and current timestamp to ensure uniqueness.

Data is not automatically tagged with run IDs, so it’s up to the user to ensure that the current run ID is retrieved and included in the data when it is reported by the service.

Getting started#

To start using run IDs, follow these steps:

  1. Configuration: Configure run ID generation for your Juice service.

  2. Generating run IDs: Generate new run IDs as needed during your experiments or measurement sessions.

  3. Retrieving run IDs: Retrieve the latest run ID.

Configuration#

To enable run ID usage in an OrangeQS Juice service, the run ID generation settings must be configured in the identifiers.toml configuration file.

[services."device"]
# This tells Juice that the run IDs for this service are generated by the service named "device".
run = "device"

To utilize the run_id within the “device” service, ensure that this service is also defined in the Juice system’s orchestration.toml configuration. For more details on services and configuration, see Adding services.

For example, add the following to orchestration.toml to set up “device” service.

[services."device"]
# No configuration options yet.

Please note that only a system administrator with access to the host system’s configuration files can perform these changes. After updating the configuration, activate the new service by running sudo juice install in the host system terminal.

After editing this file, check that the configuration is valid by running the following command in a JupyterLab terminal. It should show the contents you just added without any errors.

juice config list identifiers

Finally, restart your Juice service to apply the new configuration using restart_service() You can run this from a Python notebook or script in JupyterLab.

from orangeqs.juice.client.service import restart_service

restart_service("device")

Generating run IDs#

To generate a new run ID for your Juice service, you can use the orangeqs.juice.identifiers.run_id module This function can only be called from the service that we just configured to generate run IDs. Start an IPython console with the Service: device kernel in JupyterLab to run the following code in the service:

# Execute this code in the device service.
from orangeqs.juice.identifiers.run_id import new_run_id

new_run_id()

By default, a run ID is just a service name and timestamp, not always easy to recognize later. You can optionally give it a human-readable name (and description) when generating it:

from orangeqs.juice.identifiers.run_id import new_run_id

new_run_id(
    name="high_coherence_paper",
    description="Data for the T1 > 1s figures",
)

Names must be unique, reusing one raises an error unless you pass allow_name_reuse=True.

Restricting run IDs to an allowed device list#

You can attach an allowed-device list to a run ID, mapping each device ID to the manufacturer it belongs to. Downstream code can then validate a device ID against the currently active run, instead of accepting any string:

from orangeqs.juice.identifiers.run_id import new_run_id

new_run_id(allowed_devices={"qpu-1": "orangeqs", "qpu-2": "partner-co"})
from orangeqs.juice.identifiers.run_id import validate_device_id

# Raises ValueError if "qpu-3" was not part of the allowed_devices mapping
# passed to the latest new_run_id() call.
validate_device_id("qpu-3")

By default, allowed_devices is optional: if omitted, validate_device_id becomes a no-op and accepts any device ID. To require allowed_devices on every new run ID, set identifiers.toml::allowed_devices_mode to "required":

allowed_devices_mode = "required"

If a device ID is later reused with a different manufacturer than a previous run recorded, new_run_id raises an error rather than silently accepting what is likely a typo.

This will generate a new run ID for the device service and set it as the current run ID.

Now that a run ID has been generated, all data reported by this service should be tagged with the current run ID. To do this, orangeqs.juice.identifiers.run_id.current_run_id function to get the current run ID can be used. Note that this function can only be called from the service that generated the run ID.

# Execute this code in the <service name> service.
from orangeqs.juice_ext.device_and_instruments import current_run_id

# Dummy example of reporting data with the current run ID.
my_data = run_measurement()
my_data["run_id"] = current_run_id()  # <-- Tag data with current run ID.
store_data(my_data)

This way, all data reported by the service can be traced back to the specific run ID under which it was generated. The current_run_id() function is inexpensive to call, it should be called every time data is reported to ensure it is always tagged with the most up-to-date run ID.

Retrieving run IDs#

To retrieve the latest run ID for your Juice service, you can use the orangeqs.juice.identifiers.run_id.latest_run_id function You can call this function from any Juice service or user container that needs to access the run ID. For example, to get the latest run ID for the device service, you can run the following code:

# Execute this code from any service or user container.
from orangeqs.juice.identifiers.run_id import latest_run_id

run_id = latest_run_id()

# Dummy example of using the retrieved run ID.
load_data_for_run(run_id)

If you also want the identifier’s name and description, not just the plain string, use latest_detailed_run_id instead:

from orangeqs.juice.identifiers.run_id import latest_detailed_run_id

identifier = latest_detailed_run_id()
print(identifier.identifier_name, identifier.identifier_description)

The latest_run_id() function is cheap to call, it should be called every time data is reported to ensure it is always tagged with the most up-to-date run ID.

Listing and searching run IDs#

To list multiple recent run IDs, rather than just the latest one, use list_latest_run_ids:

from orangeqs.juice.identifiers.run_id import list_latest_run_ids

list_latest_run_ids()

For the full detail (including name and description) instead of just the plain identifier strings, use list_detailed_latest_ids:

from orangeqs.juice.identifiers.run_id import list_detailed_latest_ids

list_detailed_latest_ids()

This returns full identifier objects.

Note

To view them as a readable table in a notebook:

import pandas as pd

pd.DataFrame(identifier.model_dump() for identifier in list_detailed_latest_ids())

If you’ve been naming your run IDs, you don’t need to remember the exact timestamp-based identifier to find one again later, you can search by name instead:

from orangeqs.juice.identifiers.run_id import search_run_ids

# Find run IDs whose name contains "coherence".
search_run_ids(contains="coherence")

# Find run IDs whose name starts with "training".
search_run_ids(starts_with="training")

# Find run IDs whose name ends with "_v2".
search_run_ids(ends_with="_v2")

search_run_ids can be called from any service or user container, same as latest_run_id.

If you provide more than one of these at once, an identifier must match all of them to be included (they’re combined with AND, not OR).

Note

To view them as a readable table in a notebook:

import pandas as pd

pd.DataFrame(identifier.model_dump() for identifier in search_run_ids(starts_with="training"))

Note

None of these naming, listing, or searching functions are run-ID-specific, the same capability is available for any identifier prefix (e.g. cycle) through the more general orangeqs.juice.identifiers module, using identifiers.new_id, identifiers.search_ids, identifiers.list_detailed_latest_ids, and identifiers.latest_detailed_id directly.