Browse Source

Wrote a load of documentation

Adam Kelly 5 years ago
parent
commit
469e475144
10 changed files with 313 additions and 10 deletions
  1. 9 0
      docs/_templates/util/footer.html
  2. 1 1
      docs/conf.py
  3. 108 0
      docs/gates.rst
  4. 13 0
      docs/guide.rst
  5. 5 4
      docs/index.rst
  6. 70 0
      docs/operations.rst
  7. 5 1
      docs/quickstart.rst
  8. 49 0
      docs/registers.rst
  9. 6 1
      qcgpu/__init__.py
  10. 47 3
      qcgpu/state.py

+ 9 - 0
docs/_templates/util/footer.html

@@ -0,0 +1,9 @@
+<script type="text/x-mathjax-config">
+    MathJax.Hub.Config({
+      tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']], displayMath: [['$$', '$$'], ['\\[', '\\]']]}
+    });
+    </script>
+
+<script type="text/javascript"
+   src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
+</script>

+ 1 - 1
docs/conf.py

@@ -91,7 +91,7 @@ html_theme = 'press'
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+# html_static_path = ['_static']
 
 # Custom sidebar templates, must be a dictionary that maps document names
 # to template names.

+ 108 - 0
docs/gates.rst

@@ -0,0 +1,108 @@
+Quantum Gates
+=============
+
+In quantum computing, gates are used to manipulate quantum registers and
+to implement quantum algorithms.
+
+Built-In Gates
+--------------
+
+There are a number of gates built into QCGPU. They can all be applied
+the same way:
+
+.. code:: python
+
+   import qcgpu
+
+   register = qcgpu.State(2)
+
+   state.h(0) # Applies the Hadamard (H) gate to the first qubit.
+   state.x(1) # Applies a pauli-x (NOT) gate to the second qubit.
+
+``h`` and ``x`` can be replaced with any of the following:
+
+-  The Hadamard gate: **h** - ``state.h(0)``
+-  The S gate: **s** - ``state.s(0)``
+-  The T gate: **t** - ``state.t(0)``
+-  The Pauli-X / NOT gate: **x** - ``state.x(0)``
+-  The Pauli-Y gate: **y** - ``state.y(0)``
+-  The Pauli-Z gate: **z** - ``state.z(0)``
+-  The CNOT gate: **cx** -
+   ``state.cx(0, 1) # CNOT with control = 0, target = 1``
+-  The SWAP gate: **swap** -
+   ``state.swap(0,1) # Swaps the 0th and 1st qubit``
+-  The Toffoli gate: **toffoli** -
+   ``state.toffoli(0, 1, 2) # Toffoli with controls = (0, 1), target = 2``
+
+These are all shorthand methods for the application of arbitrary gates.
+For example, the application of a Hadamard gate above is shorthand for
+
+.. code:: python
+
+   import qcgpu
+
+   h = qcgpu.gate.h()
+
+   register = qcgpu.State(5)
+   register.apply_gate(h, 0)
+
+You can also use any of the gates as controlled gates. For example, the
+application of the CNOT gate above is shorthand for
+
+.. code:: python
+
+   import qcgpu
+
+   x = qcgpu.gate.x()
+
+   register = qcgpu.State(5)
+   register.apply_controlled_gate(x, 0, 1)
+
+Applying A Gate To A Whole Register
+----------------------------------
+
+There is a convenience method to apply a gate to every qubit in the register.
+The following applies a Hadamard gate to the whole register,
+
+.. code:: python
+
+   import qcgpu
+
+   h = qcgpu.gate.h()
+
+   register = qcgpu.State(5)
+   register.apply_all(h)
+   
+
+User Defined Gates
+------------------
+
+Gates in QCGPU are represented by the ``qcgpu.Gate`` class.
+
+The only gates that can be defined by the user are single qubit gates.
+
+The process of creating a gate is
+
+.. code:: python
+
+   import qcgpu
+   import numpy as np
+
+   gate_matrix = np.array([
+       [1, 0],
+       [0, np.exp(1j * np.pi / 4)]
+   ])
+
+   gate = qcgpu.Gate(gate_matrix)
+
+The input to the ``Gate`` constructor is checked to be a 2x2 unitary
+matrix.
+
+This newly created gate can then be applied the long hand way,
+
+.. code:: python
+
+   import qcgpu 
+
+   register = qcgpu.State(2)
+   register.apply_gate(gate, 0)

+ 13 - 0
docs/guide.rst

@@ -0,0 +1,13 @@
+==========
+User Guide
+==========
+
+This section covers the full usage of QCGPU.
+It will also contain some information about what each part represents.
+
+.. toctree::
+   :maxdepth: 2
+   
+   registers
+   gates
+   operations

+ 5 - 4
docs/index.rst

@@ -1,3 +1,4 @@
+=====
 QCGPU
 =====
 
@@ -23,16 +24,16 @@ Read the `research paper`_.
 .. _`research paper`: https://arxiv.org/abs/1805.00988
 
 
-
 Table of Contents
 ==================
 
 .. toctree::
-   :maxdepth: 2
+   :maxdepth: 3
    
    install
-   Getting started <quickstart>
-   Software reference <_autodoc/qcgpu>
+   quickstart
+   guide
+   
 
 .. Python Modules
     ==============

+ 70 - 0
docs/operations.rst

@@ -0,0 +1,70 @@
+Quantum Operations
+==================
+
+There are a number of operations you can perform on quantum registers
+with QCGPU.
+
+Measurement
+-----------
+
+Measurement of a register in QCGPU doesn’t collapse the state (although
+this may be added in the future). When you measure the state, you can
+specify the number of times to sample. The output of this ``measure``
+function is a dictionary with the bitstrings of the outputs, along with
+the number of times they were measured.
+
+You can measure a register as follows,
+
+.. code:: python
+
+   import qcgpu
+
+   register = qcgpu.State(5)
+
+   register.measure(samples=1000)
+   # {'00000': 1000}
+
+There is also a convenience method to measure only a single qubit.
+Again, the state is not collapsed
+
+.. code:: python
+
+   import qcgpu
+
+   register = qcgpu.State(5)
+
+   register.h(0)
+
+   register.measure_qubit(0, samples=1000)
+   # {'1': 523, '0': 477}
+
+Probability
+-----------
+
+QCGPU provides another method for getting the probability of each
+outcome.
+
+The probability of getting an outcome :math:`j` from a state
+:math:`\lvert \psi \rangle = \sum_{j = 0}^{2^n - 1} \alpha_j \lvert j \rangle`
+is
+
+.. math::
+
+
+   P(j) = \lvert \alpha_j \lvert^2
+
+The method ``probabilities`` returns a numpy array with each of the
+values corresponding to :math:`\lvert \alpha_j \lvert ^2` for each index
+:math:`j`.
+
+.. code:: python
+
+   import qcgpu
+
+   register = qcgpu.State(1)
+   register.h(0)
+
+   register.probabilities() # [0.5, 0.5]
+
+This method is particularly useful to plot the probabilities of each
+outcome.

+ 5 - 1
docs/quickstart.rst

@@ -1,3 +1,4 @@
+===============
 Getting Started
 ===============
 
@@ -35,4 +36,7 @@ along with how often they occurred.
 
 .. code-block:: python
 
-    {'00': 486, '11': 514}
+    {'00': 486, '11': 514}
+
+There are a few different ways to do things using QCGPU, 
+so you should check out the rest of the documentation too

+ 49 - 0
docs/registers.rst

@@ -0,0 +1,49 @@
+=================
+Quantum Registers
+=================
+
+QCGPU provides a class to represent the register, ``qcgpu.State``. The
+register class stores (on the OpenCL device) a state vector. This state
+vector is a chunk of memory, the size of which is:
+
+.. math::
+
+
+   64 \cdot 2^n \text{ bits}.
+
+This means you would need just 2kb of memory to have a 5 qubit register,
+a 30 qubit register would take up 9gb of memory.
+
+This is something to be aware of, as the state vector must fit in the
+memory of the device you wish to use.
+
+Using the ``State`` class
+-------------------------
+
+To create a new register, you can use
+
+.. code:: python
+
+   import qcgpu
+
+   register = qcgpu.State(5)
+
+This will create a 5 qubit register.
+
+When you run this, you may be prompted to choose a device. This is
+normal, as you can have more than 1 device that supports OpenCL in your
+computer. Just choose the one you want.
+
+Mathematical Description
+-------------------------
+
+This class represents a state vector :math:`\lvert \psi \rangle` with
+
+.. math::
+
+
+   \lvert \psi \rangle = \sum_{j = 0}^{2^n - 1} \alpha_j \lvert j \rangle
+
+where :math:`n` is the number of qubits, :math:`\alpha_j` is the
+amplitude and the state is :math:`j` runs overall :math:`2^n` basis
+states.

+ 6 - 1
qcgpu/__init__.py

@@ -1,2 +1,7 @@
+"""
+QCGPU Base module
+"""
+
 import qcgpu.gate
-from qcgpu.state import State
+from qcgpu.state import State
+from qcgpu.gate import Gate

+ 47 - 3
qcgpu/state.py

@@ -1,15 +1,51 @@
+"""
+Quantum Register Object
+"""
+
 from qcgpu.backend import Backend
 import pyopencl as cl
 import numpy as np
 
 class State:
-    """  Creates a new quantum register.
+    """A class for representing quantum registers.
+
+    The State class is the QCGPU representation of a quantum 
+    register / state vector.
+
+    This class is what should be used to perform the simulations,
+    and has method for things such as applying gates, measurements,
+    getting probabilities and such.
+
+    As QCGPU uses OpenCL, you may be queried about which OpenCL device
+    to use. This will only happen when running things such a python repl,
+    or running a script using QCGPU from the command line. Otherwise, a
+    device will be chosen heuristically.
+
+    When the register is created, it will be left in the state
+
+    .. math::
+
+        \\lvert 000 \\dots 0 \\rangle
+
+    With the given number of qubits.
 
     Args:
-        num_qubits (int): The number of qubits to create in the register
+        num_qubits (int): The number of qubits to create in the register.  
+            This must be greater then zero.
 
     Returns:
-        State: A representation of the quantum register
+        State: A representation of the quantum register.
+
+    Examples
+        >>> qcgpu.State(3)
+            Choose platform:
+            [0] <pyopencl.Platform 'NVIDIA CUDA' at 0x2f22390>
+            Choice [0]:0
+            Set the environment variable PYOPENCL_CTX='0' to avoid being asked again.
+            [[array(1.+0.j, dtype=complex64)]
+             [array(0.+0.j, dtype=complex64)]
+             [array(0.+0.j, dtype=complex64)]
+             [array(0.+0.j, dtype=complex64)]]
     """
     def __init__(self, num_qubits):
         
@@ -18,10 +54,18 @@ class State:
         if num_qubits <= 0:
             raise ValueError("num_qubits must be a positive integer")
 
+        #: The number of qubits that are in the register
         self.num_qubits = num_qubits
         self.backend = Backend(num_qubits)
 
     def apply_gate(self, gate, target):
+        """Applies a single qubit unitary gate to the register.
+
+        Args:
+            gate (~qcgpu.Gate): The gate to be applied to the register
+            target (int): The index of the qubit in the register that the gate
+                is to be applied to.
+        """
         if not isinstance(target, int) or target < 0:
             raise ValueError("target must be an int > 0")