.. _usage:

=============
Basic Usage
=============

This package uses gRPC messaging to interface with the device. The following sections cover basic usage patterns for sampling and system monitoring.


uQRNG Sampling
==============

uQRNG offers two types of sampling for users: :meth:`uqrng_direct.client.UqrngClient.GetEntropy`, which allows users to sample uniform random bits, and :meth:`uqrng_direct.client.UqrngClient.GetNoise`, which enables sampling from uniform random qudits with values ranging from 0 to 99,999. These qudits are used to generate the uniform random bits provided by :meth:`uqrng_direct.client.UqrngClient.GetEntropy`. In order to sample from the device must fill in the IP address specific to your device in place of "YOUR DEVICE_IP_ADDRESS" in each of the below examples.


Pulling random bits
-------------------

The device streams random bits as bytes. Below is a basic pattern on how to get
random bits from the uqrng device and convert it into a variety of formats:

.. code-block:: python

    from uqrng_direct.client import UqrngClient
    # MUST FILL IN "YOUR DEVICE IP ADDRESS" based on your devices network location
    qrng_client = UqrngClient(ip_address="YOUR DEVICE IP ADDRESS")
    qrn_bytes = qrng_client.GetEntropy(bits_of_entropy = 32*2)
    print("As bytes:", qrn_bytes)
    print("As bitstring:", qrn_bytes.decode("utf-8"))
    # convert to four 32 bit integers
    print("As integer:", [int(qrn_bytes[i:i+32], 2) for i in range(0, 32*2, 32)])

**Output:**

.. code-block:: text

    As bytes: b'1101000001101101010111100001011000101100001100100111111011110010'
    As bitstring: 1101000001101101010111100001011000101100001100100111111011110010
    As integer: [3496828438, 741506802]

    
Reading Raw Data from the Device
--------------------------------


Similarly, users can sample from the underlying uniform qudits (0-99,999) as follows:

.. code-block:: python

    from uqrng_direct.client import UqrngClient

    ip_address = "YOUR_DEVICE_IP_ADDRESS"
    uqrng_client = UqrngClient(ip_address="YOUR DEVICE IP ADDRESS",
                               port="YOUR DEVICE PORT")
    qrn_raw = uqrng_client.GetNoise(number_of_samples_requested=10)
    print(qrn_raw)

**Output:**

.. code-block:: text

    [75924, 96750, 31464, 66745, 97927, 95729, 44771, 43926, 20887, 80849],


Additional Sampling Parameters
------------------------------

Since each request is processed on demand, if two users attempt to access the device to sample simultaneously, then the device will return a gRPC *UNAVAILABLE* error with the message *"QRNG currently in use"*. However, both of the above functions have parameters to allow users to wait either indefinitely or a specific time frame for their request to be processed:

- **wait**: *bool* - whether to wait for device to become available
- **timeout**: *int* - seconds to wait for QRNG device to become available. If is less than or equal to 0, then the request waits indefinitely.



Health Tests and System Monitoring
==================================

There are several utility functions provided to support system monitoring and running health tests.

Checking System Status
----------------------

Below is an example of how to request the uQRNG device’s current system status:.

.. code-block:: python

    qrng_client.SystemStatus()

**Output:**

.. code-block:: text

    {'status_code': 1, 'status_desc': 'SAMPLING'}


For more information on possible device statuses see :class:`uqrng_direct.utils.SysStatus`.

Requesting System Information
-----------------------------

System information includes several useful fields about the uQRNG device including the current server version and the delay in minutes between schedule health tests. For full details see :class:`uqrng_direct.utils.SystemInfoDict`.

.. code-block:: python

    qrng_client.SystemInfo()

**Output:**

.. code-block:: text

    {'server_version': 'v1.0.0', 'device_type': 'uQRNG', 'test_interval_mins': 0}


Running Health Tests On-Demand
------------------------------

The device also supports running NIST SP 800-22 STS version 2.1.2 which is a comprehensive suite developed by the National Institute of Standards and Technology (NIST) for testing the randomness of binary sequences. It is designed to evaluate the quality of random number generators (RNGs) and cryptographic sequences. For more information on the tests, see `here <https://csrc.nist.gov/projects/random-bit-generation/documentation-and-software>`_. The device is configured to run all tests on 10 bitstreams of 1 million bits each. Each health test takes roughly 10 minutes to run. However, the device will be available during health testing because after samples are collected, the health tests will run in the background. There are two methods to initiate a health test: on demand and through the scheduling system. When a health test starts, it clears the results of the previous test to prevent users from retrieving outdated information. Below is example code to wait for an on-demand health test:

.. code-block:: python
		
    health_res = qrng_client.HealthTest(wait=True)


Scheduling Health Tests
-----------------------

As seen in the above :meth:`uqrng_direct.client.SystemInfo` response there is a field which tells what is the current number of minutes between each set of consecutive health tests. The default for this interval is 0 which indicates that health tests will not be run going forward. Users can configure the interval on which health tests will be run by doing the following:

.. code-block:: python

    sched_status = qrng_client.ScheduleHealthTest(test_interval_mins=120)
    print(qrng_client.SystemInfo())

**Output:**

.. code-block:: text

    {'server_version': 'v1.0.0', 'device_type': 'uQRNG', 'test_interval_mins': 120}

Whenever the system is shut down, the health testing schedule reverts to its default settings. By default, a single health test runs at startup with no additional tests scheduled. Any desired ongoing test scheduling must be reconfigured after your device is powered on.


Health Test Results
-------------------

Health test results can be retrieved using the :meth:`uqrng_direct.client.UqrngClient.FetchHealthTest` function or by employing the wait option for :meth:`uqrng_direct.client.UqrngClient.HealthTest`. The :meth:`uqrng_direct.client.Uqrngclient.FetchHealthTest`} function retrieves the most recent test result for the device.

.. code-block:: python

    health_res = qrng_client.FetchHealthTest()
    print("Dict of Results:\n", health_res)
    print("Health result table:\n", health_res["summary_table"])

**Output (truncated):**

.. code-block:: text

    Dict of Results:
     {'all_pass': True, 
     'test_detail': {
	'elapsed_time_mins': 29.21890640258789, 
	'test_name': [
	    'Frequency', 
	    'BlockFrequency', 
	    ... # truncated
	    , 'LinearComplexity'], 
	'p_value': [0.5341460108757019,  
	    ... # truncated
	    0.12232500314712524], 
	'proportion_pass': [1.0, 
	    ... # truncated
	    1.0], 
	'passed': [True,

	    True]}, 
	'summary_table': 
	    'STATISTICAL_TEST       | P-VALUE  | PROPORTION | PASS
	... # truncated
	    | True\nLinearComplexity       | 0.122325 | 1.0        | True\n'}

    Health result table:
     STATISTICAL_TEST       | P-VALUE  | PROPORTION | PASS
    -----------------------------------------------------
    Frequency              | 0.534146 | 1.0        | True
    BlockFrequency         | 0.350485 | 1.0        | True
    NonOverlappingTemplate | 0.0      | 1.0        | True
    ... # truncated
    Serial                 | 0.350485 | 0.9        | True
    LinearComplexity       | 0.122325 | 1.0        | True

A truncated example of health test output is shown above to see full description of the health test results object see :class:`uqrng_direct.utils.ResultNIST`.


Understanding Health Test Results
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

In conducting randomness tests, it is not uncommon to observe occasional failures due to random chance. To mitigate this phenomenon, adjustments such as analyzing multiple tests and observing the distribution of p-values are often employed. If failures are repeatedly observed in the same test, this raises concerns about the underlying entropy. Additionally, the sample size used is crucial in determining the overall validity of a battery of tests. It is important to consider the number of tests being run and apply an appropriate multiple test correction to the resulting p-values. Since most entropy tests are statistical in nature, it is important to remember that even a true random number generator (tRNG) will occasionally fail a test when a sufficient number of tests are conducted, due to the inherent probabilities of false positives. For further guidance, QCi recommends reading Chapter 10 of the book *Beautiful Testing*, available as a `free PDF <https://www.johndcook.com/Beautiful_Testing_ch10.pdf>`_. For further guidance on appropriate testing methodology and implementation for randomness testing, we also offer paid consulting services. Please contact our sales team by submitting the `Contact Form <https://quantumcomputinginc.com/contact>`_ on our website with subject "QRNG CONSULTING".


Troubleshooting and Support
===========================

If you encounter any issues with your uQRNG device or this package contact our `customer support <https://quantumcomputinginc.ladesk.com/>`_ for assistance.
