Basic Usage
Formatting input polynomial
Dirac has the ability to solve higher order polynomials with many variables. In order to illustrate the input format that is used for submitting these problems we will utilize a simple example polynomial:
The submission input is split into two parts the coefficients and the set of variable indices associated with each coefficient. As an example for above the leading submission coefficients would be created as follows:
- poly_coefs = [3, 2.1, 1.5, 7.9, 1, 1]
The set of variable indices associated with each coefficient will be represented as follows:
- poly_indices = [[0,0,4], [0,1,1], [0,2,2], [0,2,3], [2,4,4], [3,3,3]]
Taking the first polynomial term, , which is represented by the first poly index set in the list, [0,0,4]
, with each 0 indicating a place holder for no variable used for a given degree in the polynomial index set and the 4 in the last place indicating the 4th variable for the input problem. Additionally, each individual polynomial variable index set has non-decreasing values moving from left to right such that for each set of indices:
must meet following condition:
This property guarantees the uniqueness of the submitted polynomial index set. Also, this sparse representation minimizes the amount of data needed to be transferred to the device to obtain a solution. If the same polynomial index set is specified more than once then the coefficients will be added together to create the final polynomial input.
A dual purpose solver
There are two separate types of of solving routines that can be utilized with Dirac-3 a sum constrained solver and an integer solver.
Integer solver
The integer solver will return solutions vector based on the associated num_levels
, , such that each solution variable will be from the following integer set:
Additionally, the current iteration of the device allows for a maximum of 954 unique optical levels. This implies when considering all num_levels
, , across all variables together they must meet the following constraint in order for the device to accept the input:
For example if considering a problem where each variable is binary then the maximum number of variables that will be accepted is 477.
A simple example of solving an integer problem based on the input polynomial that was constructed above can be seen below:
- from eqc_direct.client import EqcClient
- # MUST FILL IN THE VALUES IN THIS FROM YOUR NETWORKING SETUP FOR THE DEVICE
- eqc_client = EqcClient(ip_address="YOUR DEVICE IP ADDRESS", port="YOUR DEVICE PORT")
- poly_coefs = [3, 2.1, 1.5, 7.9, 1, 1]
- poly_indices = [[0,0,4], [0,1,1], [0,2,2], [0,2,3], [2,4,4], [3,3,3]]
- # use lock to prevent other users from taking exclusive access to the device
- lock_id, start_ts, end_ts=eqc_client.wait_for_lock()
- try:
- result_dict = eqc_client.solve_integer(
- lock_id=lock_id,
- poly_indices = poly_indices,
- poly_coefficients = poly_coefs,
- relaxation_schedule = 2,
- num_levels = [2,2,3,4])
- # release lock when finished using the device
- finally:
- lock_release_out = eqc_client.release_lock(lock_id=lock_id)
For more information on additional parameters see Advanced Usage and eqc_direct.client.EqcClient.solve_integer()
.
To learn about the response for this solve routine see eqc_direct.client.IntegerResult
.
Sum constrained solver
The sum constrained solver solves for the objective over an affine simplex such that for each solution and sum constraint :
This implication should be taken into account when analyzing solutions and selecting appropriate problems for this device.
Another important feature for the sum-constrained solver is the solution_precision
, which distills the solution so that each
is rounded to the unit of precision specified. For example, if solution_precision = 0.5
and sum_constraint = 10
, for a solution with values
, one possible distilled solution would be .
Notice that the distilled solution preserves the sum_constraint
while ensuring that each value in the solution is divisible by the solution_precision
.
A simple example of solving a sum constrained problem using the eqc_direct.client.EqcClient.solve_sum_constrained()
can be seen below:
- from eqc_direct.client import EqcClient
- # MUST FILL IN THE VALUES IN THIS FROM YOUR NETWORKING SETUP FOR THE DEVICE
- eqc_client = EqcClient(ip_address="YOUR DEVICE IP ADDRESS", port="YOUR DEVICE PORT")
- poly_coefs = [3, 2.1, 1.5, 7.9, 1, 1]
- poly_indices = [[0,0,4], [0,1,1], [0,2,2], [0,2,3], [2,4,4], [3,3,3]]
- # use lock to prevent other users from taking exclusive access to the device
- lock_id, start_ts, end_ts=eqc_client.wait_for_lock()
- try:
- result_dict = eqc_client.solve_sum_constrained(
- lock_id=lock_id,
- poly_indices = poly_indices,
- poly_coefficients = poly_coefs,
- relaxation_schedule = 2,
- solution_precision=1,
- sum_constraint = 100)
- finally:
- # release lock when finished using the device
- lock_release_out = eqc_client.release_lock(lock_id=lock_id)
Each time a job is processed, it blocks for the duration of the device run when using this call. For information regarding results object, see eqc_direct.client.SumConstrainedResult
.
System monitoring
eqc_direct.client.EqcClient.system_status()
returns status_code
and status_desc
. For details, see eqc_direct.utils.SysStatus
. In general, status_code
greater than or equal to 3 indicates the system is currently in an errored state. See device manual for how to trouble shoot issues based on status_code
. Displayed below is a simple system status check followed by a print out ofversions associated with device from eqc_direct.client.EqcClient.system_info()
:
- from eqc_direct.client import EqcClient
- eqc_client = EqcClient(ip_address="YOUR DEVICE IP ADDRESS", port="YOUR DEVICE PORT")
- curr_sys_status = eqc_client.system_status()
- sys_info = eqc_client.system_info()
- print("System status:", curr_sys_status)
- print("System info:", sys_info)
Device Lock Basics
Device locking ensures that only one process is running on the device at a time. In order to ensure this functionality, there are a few useful functions:
eqc_direct.client.EqcClient.check_lock()
-allows for user to check lock status without attempting acquisitioneqc_direct.client.EqcClient.acquire_lock()
-makes a single attempt to acquire the device lockeqc_direct.client.EqcClient.release_lock()
-releases lock if lock currently held by the user.
In order to ensure access to users there is a timer on the server that if a user is inactive for more than one minute and another user wishes to use the device they will be able to acquire the device
lock and the previous idle user will lose it’s lock on the device. In order to help ease the process of acquiring the lock it’s recommended to use
eqc_direct.client.EqcClient.wait_for_lock()
. In order to ensure maximum device availability it is a good practice to release the lock if taking time between runs on the device.
Health monitoring and calibration for Dirac
Dirac devices periodically automatically calibrate to ensure that it is able to produce high quality results consistently. Similarily, health tests also run on set intervals to ensure that the device is functioning properly. If either of these operations fails the device will enter a hardware fail state see eqc_direct.utils.SysStatus
for various status codes associated to this state. If the device enters an error state try power cycling the device with switch located on back panel. This will cause the device to calibrate and perform the health testing suite. If hardware failure state persists then contact QCi for further support https://quantumcomputinginc.ladesk.com