.. _usage:

=============
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:

.. math::
    e = 3 x_4+2.1 x_1^2+1.5 x_2^2+7.9 x_2 x_3+x_2 x_4^2+ x_3^3


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:

.. code-block:: python

   poly_coefs = [3, 2.1, 7.9, 1.5, 7.9, 1]

The set of variable indices associated with each coefficient will be represented as follows:

.. code-block:: python

   poly_indices = [[0,0,4], [0,1,1], [0,2,2], [0,2,,3], [2,4,4], [3,3,3]]

Note, that each individual polynomial variable index set has non-decreasing values moving from left to right such that for each set of indices:

.. math::
   [k_1, k_2, k_3, \dots, k_{n-1}, k_n]

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. Additionally, the input format allows users to submit the polynomial in any order that they choose as long as no two sublists are identical.
 

Device sum constraint for problem
----------------------------------

For Dirac 3 device, a summation constraint must be applied. This summation constraint implies that each solution vector must add up to a specific constant.
This is due to a normalization process that is required for these specific devices.
This implication should be taken into account when analyzing solutions and selecting appropriate problems for this device.


Solving problems using Dirac
------------------------------

A simple example of submitting a job using the :meth:`eqc_direct.client.EqcClient.process_job` can be seen below. To run, you must fill in your device's ip address and port in the example below.


.. code-block:: python

    from eqc_direct.client import EqcClient
    
    # MUST FILL IN THE VALUES IN THIS FROM YOUR NETWORKING SETUP FOR THE DEVICE
    eqc_client = EqcClient(ip_addr="YOUR DEVICE IP ADDRESS",
			   port="YOUR DEVICE PORT")
    poly_coefs = [3, 2.1, 7.9, 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]]
    # using lock prevent other users from  attempting to gain exclusive access to the device
    lock_id, start_ts, end_ts=eqc_client.wait_for_lock()
    result_dict = eqc_client.process_job(
	lock_id=lock_id,
	poly_indices = poly_indices,
	poly_coefficients = poly_coefs,
	relaxation_schedule = 2,
	solution_precision=1,
	sum_constraint = 100)
    # release lock when are 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 :class:`eqc_direct.client.EqcResult`.
	
System monitoring
------------------

:meth:`eqc_direct.client.EqcClient.system_status` returns :code:`status_code` and :code:`status_desc`. For details, see :class:`eqc_direct.utils.SysStatus`. In general, :code:`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 :code:`status_code`. Displayed below is a simple system status check:

.. code-block:: python

    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()
      

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:

  * :meth:`eqc_direct.client.EqcClient.check_lock`-allows for user to check lock status without attempting acquisition
  * :meth:`eqc_direct.client.EqcClient.acquire_lock`-makes a single attempt to acquire the device lock
  * :meth:`eqc_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
:meth:`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 :class:`eqc_direct.utils.SysStatus` for various status codes associated to this state. If the device enters an error state try to turn powering the device off and then turnning it device back on. This will cause the device to calibrate and perform the health testing suite. If hardware failure state persists then contact QCi for further technical contact https://quantumcomputinginc.ladesk.com
