walkthrough_1_introduction

# Walkthrough 1: Introduction to Oqtant and Oqtant Quantum Matter Services (QMS)¶

## Introduction¶

Welcome to `Oqtant`, a python-based object-oriented interface for creating, submitting, and retrieving results from experiments with ultracold quantum matter carried out on Infleqtion's Oqtant platform.

In this introductory tutorial, we will explore the basics of how you, the user, interacts with Oqtant's quantum matter services (QMS) using Oqtant. The general workflow is as follows:

1. Create an Oqtant account: https://oqtant.infleqtion.com
2. Download and install Oqtant (this!): https://pypi.org/project/oqtant/
3. Instantiate an instance of the `OqtantClient`, aka the client, which facilitates communication with QMS (requires authenticating your account).
4. Create a user-defined `QuantumMatter`, aka matter, object for making and/or manipulating quantum matter.
5. Submit your matter object to the client. This creates a `OqtantJob`, aka a job, that runs on Oqtant's hardware platform(s) and returns a `job id` (UUID or id) that can be used for checking job status and retrieving results.
6. When your job is complete, retrieve and analyze results.

For more information, please refer to our documentation: https://gitlab.com/infleqtion/albert/oqtant/-/blob/main/README.md
Also see our companion web application https://oqtant.infleqtion.com/ for quick access to job creation, results, and account management.
Support, feature requests, and bug reports can be submitted here: https://oqtant.infleqtion.com/support

This walkthrough focuses on how to use Oqtant in conjunction with Oqtant QMS. Follow-on walkthroughs will explore user options and Oqtant data structures in significantly more detail.

Note: execution of this entire walkthrough will use quantity 2 of your job quota.

### Imports and instantiation of the OqtantClient (requires account authentication)¶

The following block of code will import the needed modules and instantiate a client object that communicates with Oqtant's server(s). Once authenticated, you can safely close that tab and return to this notebook.

NOTE: instantiating the client creates a temporary web server on your machine to handle authenticating with Oqtant QMS. By default, this server will run on port 8080. If that port is unavailable, you can edit default port by passing an port parameter with any value in the range of 8080--8085.

In [ ]:

    from oqtant.oqtant_client import get_client
    from oqtant.schemas.quantum_matter import QuantumMatter

    client = get_client(port=8081)

## Your first QuantumMatter object and OqtantJob¶

Basic usage of Oqtant involves the creation of a QuantumMatter object, which captures the user-defined inputs that control the behavior of Oqtant hardware. We will explore the available options and underlying data structures in detail in following walkthroughs.

### Instantiate a QuantumMatter object¶

For now, we will create a simple object that creates quantum matter at a user-defined target temperature (in nK). We will also give our object a (hopefully meaningful, but optional) name.

In [ ]:

    matter = QuantumMatter(
        temperature=100, name="my first quantum matter"  # target temperature (in nK)
    )

### Submit your QuantumMatter object to the OqtantClient to generate an OqtantJob¶

The next step is to submit our QuantumMatter object to the client using the `OqtantClient.submit()` method. This will create an `OqtantJob` that will enter the QMS job queue and run on Oqtant hardware when it becomes available. The method itself returns a unique job id (32 character UUID string). Further managment of the job, including retrieving results, will require this id. Here, we will explicitly track the status of our job in the queue by passing the track=True option. The default value is False.

In [ ]:

    # submission returns the job uuid, e.g. "b26095ac-58bc-4308-a096-13a2544be722"
    my_job_id = client.submit(matter, track=True)

    Submitting 1 job(s):

    - Job: my first quantum matter
      ID: 3d9da73b-db66-46a4-ae26-117230eeee67
      Queue Position: 294
      Estimated Time: 588 minutes

    Tracking 1 job(s):

    - Job: my first quantum matter
      - RUNNING
      - COMPLETE

    All job(s) complete

We can see the job progress through being submitted to the queue (PENDING), being executed on the hardware (RUNNING) and finishing (COMPLETE). The amount of time this sequence takes will depend on the current load on QMS and whether your account is deducting from priority (paid) or free quotas.

### Retrieve/fetch the COMPLETE job¶

Once a job is complete, we can proceed to fetching the results. This is accomplished by using the client to get the job (including the results) from the Oqtant server by passing the job id as a parameter to the `OqtantClient.get_job()` method. If your job is not yet complete, you can still fetch the job from the server but the output fields will not be populated.

In [ ]:

    # get job from server, including results if available
    # my_job_id = "b26095ac-58bc-4308-a096-13a2544be722"
    my_job = client.get_job(my_job_id)

### Extracting, visualizing, and analyzing job results¶

#### Output data fields¶

Once we have a complete job object, we can then extract the result / output using the `OqtantJob.output` field. The contents of the output data depends on the initially constructed and submitted matter object. In this example, which uses the default time-of-flight imaging option, the output data contains the following items:

In [ ]:

    print(my_job.output_fields)

    - mot_fluorescence_image
      - pixels
      - rows
      - columns
      - pixcal
    - tof_image
      - pixels
      - rows
      - columns
      - pixcal
    - tof_fit_image
      - pixels
      - rows
      - columns
      - pixcal
    - tof_fit
      - gaussian_od
      - gaussian_sigma_x
      - gaussian_sigma_y
      - tf_od
      - tf_x
      - tf_y
      - x_0
      - y_0
      - offset
    - tof_x_slice
      - points
    - tof_y_slice
      - points
    - total_mot_atom_number
    - tof_atom_number
    - thermal_atom_number
    - condensed_atom_number
    - temperature_nk
    None

In this case, we can see results from a fluorescence image of the magneto-optical trap (mot), a cooling stage early in the sequence that produces the quantum matter that is useful for diagnosing number drifts etc., a time-of-flight (tof) absorption image with pixel data, row/column counts, and a pixel calibration (pixcal, in microns/pixel), a fit version of the tof image based on the calculated thermal/condensed atom populations and the associated temperature. These latter results are derived from fitting a "bimodal" distribution, consisiting of a sum of Gaussian (thermal/classical phase) and Thomas-Fermi (condensed phase) distributions, to the resulting (time of flight) image of the atoms. Data corresponding to the bimodal fit is included in the tof_fit field. All tof image results are given in terms of the optical depth (OD).

Any of the output data contents can be accessed programmatically, e.g.

In [ ]:

    print("temperature (nK):", my_job.output.temperature_nk)
    print("thermal atom population:", my_job.output.thermal_atom_number)
    print("condensed atom population:", my_job.output.condensed_atom_number)

    temperature (nK): 192
    thermal atom population: 8559
    condensed atom population: 2611

#### Extracting output image data¶

Of course, you may want to calculate the quantities above, or any number of others, using the data from the tof (absorption) image of the atoms directly. Raw image data can be accessed programatically, as above, or using the `OqtantJob.get_image_data()` helper method. The type of image data to be fetched is passed as an input parameter:

In [ ]:

    mot_image_data = my_job.get_image_data(image="MOT")
    tof_image_data = my_job.get_image_data(image="TIME_OF_FLIGHT")
    tof_fit_image_data = my_job.get_image_data(image="TIME_OF_FLIGHT_FIT")

In either approach, image data is returned as a 2D numpy array of values for integer-valued pixel positions. These pixel positions can be converted to position space by accessing the OqtantJob.pix_cal field, which has the units of microns/pixel.

We also have access to pre-calculated cuts of the time-of-flight image along the detected center/peak of the atom ensemble:

In [ ]:

    x_slice = my_job.get_slice(axis="x")
    y_slice = my_job.get_slice(axis="y")

#### Visualizing results¶

There are a number of tools/methods built into Oqtant for visualizing job outputs.

In [ ]:

    my_job.plot_tof(figsize=(6, 6))
    my_job.plot_slice(axis="x")

    Peak OD:  0.970856356851433

These in-built plots show the image of the atoms taken at the end of the experiment, in units of optical depth, as well as cuts along the x and y axes. These latter two plots include solid curves corresponding to the bimodal fit that includes the contributions from the quantum matter's thermal fraction (red curve) and thermal + condensed combination (blue curve).

Of course, the raw data can be used directly for further customization and analysis:

In [ ]:

    import matplotlib.pyplot as plt

    TOF_data = my_job.get_image_data("TIME_OF_FLIGHT")
    plt.figure(figsize=(6, 6))
    plt.title("Customized plot of the same OD results")
    TOF_plot = plt.imshow(TOF_data, origin="lower", cmap="nipy_spectral")
    plt.grid(visible=True)
    plt.xlabel("x position (pixels)")
    plt.ylabel("y position (pixels)")
    plt.colorbar(TOF_plot, shrink=0.8)
    plt.show()

## Job management using the local filesystem¶

While it is completely possible to always retrieve jobs from the Oqtant server, provided they have not been deleted or removed by the user, when dealing with large sets of jobs, or without network connectivity, it is often more convenient and efficient to work with the job data on your local machine.

### Saving a job to file¶

To easily reference OqtantJobs from the current or previous sessions it is useful to write their information to a local file. For this, you can use the `client.write_job_to_file()` method:

In [ ]:

    client.write_job_to_file(my_job)

    Wrote file: "3d9da73b-db66-46a4-ae26-117230eeee67.txt"

By default, this method will save the job data in a newly created local file with the name `*job id*.txt` in the same directory as the current walkthrough / jupyter notebook. Alternatively, you can customize the resulting filename as follows:

In [ ]:

    client.write_job_to_file(
        my_job, file_name=my_job.name  # use job name as filename instead
    )

    Wrote file: "my first quantum matter.txt"

Full control of data saving is made available by providing the full path of the desired output file:

In [ ]:

    import os

    home = os.path.expanduser("~")

    client.write_job_to_file(
        my_job, file_path=home + "/Documents/" + str(my_job.id) + ".txt"
    )

    Wrote file: "/home/noah_fitch/Documents/3d9da73b-db66-46a4-ae26-117230eeee67.txt"

Take care not to accidentally write over existing local job data files, especially when specifying custom filenames. Using the default of the job id should help reduce this possibility since that field is unique for each job.

### Loading a job from file¶

Retrieving a job from a local file is accomplished by specifying the (local directory) filename or the full file path:

In [ ]:

    job_from_file = client.load_job_from_file(str(my_job.id) + ".txt")

    Wrote file: "3d9da73b-db66-46a4-ae26-117230eeee67.txt"

### Automatically saving a job to file¶

## Additional details, features, and discussion¶

### Including user notes¶

Every QuantumMatter object, and hence also every associated OqtantJob, can hold a note up to 500 characters long. This can be used to add context and additional information. A note remains tied to the job and can be referenced later.

In [ ]:

    matter_with_note = QuantumMatter(
        temperature=100,
        name="matter with a note",
        note="This is something special that I would like to remember.",  # NEW!
    )

### Mapping of the QuantumMatter object to a `JobType`¶

Each QuantumMatter object submitted to the client is mapped to a `JobType` that depends on what features and options are specified. This allows Oqtant to enable or disable certain hardware features depending on what the user requires for that particular run of the hardware. We will explore the different job types in future walkthroughs. For our example shown here, our simple QuantumMatter object maps to a BEC job:

In [ ]:

    print(my_job.job_type)

    BEC

The full list of available job types, not all of which are currently supported by the hardware, are as follows:

In [ ]:

    from bert_schemas import job

    for type in job.JobType:
        print(type.value)

    BEC
    BARRIER
    BRAGG
    TRANSISTOR
    PAINT_1D

### Searching for previous jobs¶

If you need to go back in time to see a history of jobs submitted in previous sessions you can use the `OqtantClient.search_jobs()` method. This method provides a way to ask Oqtant for jobs associated to your account that match certain search criteria.

The supported search values are:

- job_type
- name
- submit_start
- submit_end
- notes

Note: submit_start and submit_end must be in ISO format to properly work (YYYY-MM-DD or YYYY-MM-DDThh:mm:ss)

Using these search values Oqtant will return to you any jobs that are associated to your account and match the provided criteria.

In [ ]:

    client.search_jobs(submit_start="2023-08-20", submit_end="2023-08-31", limit=3)

Out[ ]:

    [{'name': 'my first program',
      'origin': 'OQTANT',
      'status': 'COMPLETE',
      'display': True,
      'qpu_name': 'UNDEFINED',
      'job_type': 'BEC',
      'external_id': 'db9a80aa-dc88-494a-93b2-e5d4378bb2f0',
      'time_submit': '2023-08-25T16:50:15.537140'},
     {'name': 'batch job name',
      'origin': 'OQTANT',
      'status': 'COMPLETE',
      'display': True,
      'qpu_name': 'UNDEFINED',
      'job_type': 'BEC',
      'external_id': 'a1076a4f-1830-47b9-aa04-7f4ee9ea8660',
      'time_submit': '2023-08-22T20:14:50.453972'},
     {'name': 'program 4 of 4',
      'origin': 'OQTANT',
      'status': 'COMPLETE',
      'display': True,
      'qpu_name': 'UNDEFINED',
      'job_type': 'BEC',
      'external_id': 'b49a2d22-8a9c-4a97-8f08-8b5fcf51b3e8',
      'time_submit': '2023-08-22T20:14:08.564534'}]

### Showing the current status of your jobs in the queue¶

When executing many jobs on Oqtant QMS, it is useful to be able to check the status of your submitted jobs without fetching each job individually and checking the status field. This can be accomplished using the OqtantClient.show_queue_status() method, which accepts the same filters as the OqtantClient.search_jobs() method explored above.

In [ ]:

    client.show_queue_status(submit_start="20230801")

    Job ID    Job Status    Time submitted    Job Name
    --------  ------------  ----------------  ----------

If you would like to also see completed jobs, you can pass the include_complete=True option:

In [ ]:

    # show queue status for the month of August 2023, including COMPLETE jobs:
    client.show_queue_status(
        submit_start="20230801",
        submit_end="20230831",
        include_complete=True,
        limit=20,  # limit the number of results shown
    )

    Job ID                                Job Status    Time submitted              Job Name
    ------------------------------------  ------------  --------------------------  ------------------------------------
    db9a80aa-dc88-494a-93b2-e5d4378bb2f0  COMPLETE      2023-08-25T16:50:15.537140  my first program
    a1076a4f-1830-47b9-aa04-7f4ee9ea8660  COMPLETE      2023-08-22T20:14:50.453972  batch job name
    b49a2d22-8a9c-4a97-8f08-8b5fcf51b3e8  COMPLETE      2023-08-22T20:14:08.564534  program 4 of 4
    dabec593-aba8-44e9-95fa-7855ac304738  COMPLETE      2023-08-22T20:14:08.161604  program 3 of 4
    7337d59f-6c7a-4761-a1f4-a70a8496e22d  COMPLETE      2023-08-22T20:14:07.772585  program 2 of 4
    72b4927f-9d3f-4ec2-a9da-2e4ae560f72c  COMPLETE      2023-08-22T20:14:07.353668  program 1 of 4
    97bd5e7e-bfcb-4baf-ae95-89746d26f3c9  COMPLETE      2023-08-22T20:11:20.525009  my first program
    4e9ae649-a449-4157-913d-6f5267049dc2  COMPLETE      2023-08-21T14:43:09.094472  batch job name
    ba574f28-6300-4210-943f-9bcd24f9efe1  COMPLETE      2023-08-21T14:23:49.598577  program 4 of 4
    6e7cbae1-2d2a-43cc-9587-a480b4b3d879  COMPLETE      2023-08-21T14:23:49.241456  program 3 of 4
    1ad0fe57-3ae0-4041-b5d5-70e6633c8c17  COMPLETE      2023-08-21T14:23:48.800969  program 2 of 4
    fe728e7b-bba5-4a1f-ae76-69cd8aca096c  COMPLETE      2023-08-21T14:23:48.292503  program 1 of 4
    01736b50-6d5d-4139-be0b-4e3a59280e95  COMPLETE      2023-08-21T04:06:21.503342  QM program w/ lifetime + tof control
    e95a4af4-5d46-4890-968e-6c04483bbf65  COMPLETE      2023-08-21T03:16:59.176233  my first program
    1b6d48f9-c586-452a-b0a3-5607441e8230  COMPLETE      2023-08-20T15:02:40.282007  my first program
    4b40ac59-285e-474e-9925-a43084644378  COMPLETE      2023-08-20T14:51:38.662446  my first program
    cc330f4c-9483-4797-a3b5-c8db469756f4  COMPLETE      2023-08-20T03:10:21.474037  batch job name
    ca1fb06f-c797-40b6-88ed-e25b6f800117  COMPLETE      2023-08-20T03:01:23.539382  batch job name
    9ddc4f37-8bca-490e-86ab-c4610d0e2c95  COMPLETE      2023-08-20T02:48:21.507451  my first program
    4080c76a-940a-4293-81f1-6413170b97bc  COMPLETE      2023-08-20T02:46:17.563003  program 4 of 4
    9bff2751-3e19-45c2-b091-6938013316ad  COMPLETE      2023-08-20T02:46:16.947324  program 3 of 4
    96d538bb-d05b-487f-b93f-63429c2b7a34  COMPLETE      2023-08-20T02:46:16.376419  program 2 of 4
    5a005462-461e-42e1-ac02-68f44754c1ae  COMPLETE      2023-08-20T02:46:15.948562  program 1 of 4
    35fcb1c0-5f25-4c58-b3ed-a89893ef7e3f  COMPLETE      2023-08-20T02:45:52.362947  my first program
    1456106d-f734-4630-bcb1-c9e3af291065  COMPLETE      2023-08-20T02:35:07.489782  program 4 of 4
    6f76b0cf-1bb0-4f0e-a4a6-f191ec556c44  COMPLETE      2023-08-20T02:35:06.613275  program 3 of 4
    66372d01-9bac-4903-a49a-a652e2cda756  COMPLETE      2023-08-20T02:35:06.237922  program 2 of 4
    44e3050d-c15d-4288-a1a6-aa04ba483db2  COMPLETE      2023-08-20T02:35:05.846521  program 1 of 4
    38cdf96a-6b01-49fd-a601-561790f04b69  COMPLETE      2023-08-19T04:20:58.257284  program 4 of 4
    36d7e970-0be3-4de0-83dd-1a4e469ed7a4  COMPLETE      2023-08-19T04:20:57.722123  program 3 of 4
    7cf65430-06a6-4671-8df5-9b1d5f6ca410  COMPLETE      2023-08-19T04:20:57.311595  program 2 of 4
    0298891d-ab2f-41c4-ba77-df0eff07c26e  COMPLETE      2023-08-19T04:20:56.880924  program 1 of 4
    bd8d9d03-fb3e-4928-8974-a65599d90947  COMPLETE      2023-08-19T04:20:24.520163  program 4 of 4
    b56ff043-7a3d-47fd-9732-8cc4cddedd29  COMPLETE      2023-08-19T04:20:24.090569  program 3 of 4
    5e6e2496-c2d0-40de-b205-7498eefed4de  COMPLETE      2023-08-19T04:20:23.707276  program 2 of 4
    05a3bbbc-00e4-4f9e-baaa-039fb35f3f46  COMPLETE      2023-08-19T04:20:23.307357  program 1 of 4
    ad18ea34-27cc-49ae-8992-57d080f2229b  COMPLETE      2023-08-19T04:12:28.797705  quantum matter program
    5cc4f782-d02b-4899-a3f7-59c2ebead0c2  COMPLETE      2023-08-19T04:12:28.278917  quantum matter program
    869cb923-f450-4559-b369-91ae1858ec1b  COMPLETE      2023-08-19T04:12:27.872695  quantum matter program
    a5991571-2bd6-430b-9941-c44c1db8b350  COMPLETE      2023-08-19T04:12:27.268903  quantum matter program
    e2b6a8b1-d097-4603-afa7-46f0e722a2c5  COMPLETE      2023-08-19T04:12:26.867518  quantum matter program
    d6744607-3a7a-4cd2-8ede-4a1071668ad5  COMPLETE      2023-08-19T04:10:35.228202  my first program
    a9b7a465-9daa-46c3-a10d-66b404cc50b8  COMPLETE      2023-08-19T04:08:47.060686  quantum matter program
    a354fe67-1001-424e-80ad-1405228552b7  COMPLETE      2023-08-19T04:08:46.646112  quantum matter program
    5f8a0607-c8e2-405c-995c-3bc2ebc1df63  COMPLETE      2023-08-19T04:08:46.190501  quantum matter program
    36072126-7e18-4b61-8e4c-8de8ad04c2f7  COMPLETE      2023-08-19T04:08:45.548713  quantum matter program
    a64badee-5f2c-4ee6-9e32-cbbf89657ca6  COMPLETE      2023-08-19T04:08:45.194942  quantum matter program
    6b35debe-6e05-478f-8fa7-e49840eb67da  COMPLETE      2023-08-19T04:08:20.433448  my first program
    e44ea84d-d35e-4d9f-af86-44e640e098a6  COMPLETE      2023-08-19T03:57:42.408209  program5
    79e06d3f-b66f-4508-84cd-7a70e55e6845  COMPLETE      2023-08-19T03:57:42.023139  program4

### Querying your current job quota¶

Currently, each user of Oqtant QMS has limits on the number of jobs they can submit per day, etc. Detailed management of your allocated job quota can be accomplished using the web app. From Oqtant, you can show your current limits as follows:

In [ ]:

    client.show_job_limits()

    daily_limit 1000
    daily_used 0
    daily_remaining 1000
    purchased None
    purchased_remaining None

### Units¶

Oqtant uses units that are natural choices for dealing with the quantum matter produced and manipulated by the Oqtant platform. The table below is a summary of relevant units used throughout this and following walkthroughs. Some of these objects/quantities have not yet been encountered, but will be shortly.

Quantity | Units | Notes
--- | --- | ---
time | milliseconds (ms, $10^{-3}$ m) |
frequency | megahertz (MHz, $10^6$ Hz) | Relative to the energetic trap bottom, $\geq 0$
powers | milliwatts (mW, $10^{-3}$ W) | Power radiated by an RF loop antenna adjacent to the cold atom sample, $\geq 0$
temperature | nanokelvin (nK, $10^{-9}$ K) |
barrier height | kilohertz (kHz, $10^{3}$ Hz) | Energetic height / Planck's constant
barrier position | microns ($\mu m$, $10^{-6}$ m) | Center of barrier
barrier width | microns ($\mu m$, $10^{-6}$ m) | Shape-dependent width parameter
landscape potential | kilohertz (kHz, $10^{3}$ Hz) | Energetic height / Planck's constant