Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/eclipse-qrisp/Qrisp into ba…
Browse files Browse the repository at this point in the history
…cktracking_upgrades
  • Loading branch information
positr0nium committed Feb 15, 2024
2 parents 8b42865 + 7e721d8 commit 3bb374c
Show file tree
Hide file tree
Showing 43 changed files with 1,242 additions and 880 deletions.
11 changes: 10 additions & 1 deletion documentation/source/general/changelog/0.4.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ With 0.4 we integrated the infrastructure to facility the implementation and com
* We wrote a :ref:`tutorial for Shor's algorithm in Qrisp <shor_tutorial>` and created a dead simple :ref:`interface for factoring numbers <Shor>`.
* :ref:`Decrypt <crypto_tools>` your neighbors pizza delivery order to make them eat pineapples! 😈

As we found out, implementations of Shor's algorithm that are able to return a QuantumCircuit in a finite amount of time are an extremely rare sight. After some searching, we could find `some <https://github.com/RevanthK/ShorsAlgorithmIBMQiskit>`_ `competitors <https://qiskit.org/documentation/stable/0.28/tutorials/algorithms/08_factorizers.html#:~:text=Shor's%20Factoring%20algorithm%20is%20one,N%20%3D%2015%20backend%20%3D%20Aer.>`_ for a benchmark:
As we found out, implementations of Shor's algorithm that are able to return a QuantumCircuit in a finite amount of time are an extremely rare sight. After some searching, we could find some competitors for a benchmark:

* `The (deprecated) Qiskit implementation <https://qiskit.org/documentation/stable/0.28/tutorials/algorithms/08_factorizers.html#:~:text=Shor's%20Factoring%20algorithm%20is%20one,N%20%3D%2015%20backend%20%3D%20Aer.>`_.
* `The implementation found in the QIBO framework <https://qibo.science/qibo/stable/code-examples/tutorials/shor/README.html>`_.
* `An implementation found in a public repository on GitHub <https://github.com/RevanthK/ShorsAlgorithmIBMQiskit>`_.

.. _shor_benchmark_plot:
|
Expand Down Expand Up @@ -66,6 +70,11 @@ Network interface

For remote backend queries, Qrisp now uses the network inteface developed in the `SequenC project <https://sequenc.de/>`_. This project aims to build a uniform, open-source quantum cloud infrastructure. Note that specific backend vendors like IBMQuantum can still be called via :ref:`VirtualBackends <VirtualBackend>`.

Docker Container
----------------

Using the new network interface, we set up a :ref:`Docker container with a bunch of simulators <DockerSimulators>`. This gives you access to 8 new simulators without having to go through the hassle of installing and converting the compilation results. You can simply call ``docker pull`` and ``docker run`` and that's it!

Minor features
--------------

Expand Down
1,270 changes: 700 additions & 570 deletions documentation/source/general/changelog/04_shor_plot.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
.. _DockerSimulators:

Docker Simulators
=================

The Qrisp network interface enables convenient access to a variety of simulators through a docker container. You can simply download the docker container and obtain access to simulation without having to fight through installation and/or conversion issues. For this you need `Docker Desktop <https://www.docker.com/products/docker-desktop/>`_. This software allows you to install and execute the most complex software environments without the hassle of compatibility issues regarding your platform. It is therefore a perfect fit for hosting quantum simulators, which can be tricky to get running. After you are done installing, please execute:

.. code-block:: console
docker pull qrisp/qrisp_sim_collection:latest
To start the docker container your run:

.. code-block:: console
docker run -p 8083:8083 -p 8084:8084 -p 8085:8085 -p 8086:8086 -p 8087:8087 -p 8088:8088 -p 8089:8089 -p 8090:8090 qrisp/qrisp_sim_collection
The ``-p`` commands open the ports of the docker container such that Qrisp can send the simulation requests. Once you have run this command, the container should appear in the Docker GUI, so you can simply press start if you need it again.

Once the container is running, you can start using the following backends on your machine:


.. list-table::
:widths: 25 50
:header-rows: 1

* - Simulator Name
- Description
* - ``CirqSim()``
- `"A sparse matrix state vector simulator that uses numpy." <https://quantumai.google/reference/python/cirq/Simulator>`_
* - ``PennylaneSim()``
- `"The default.qubit device is PennyLane’s standard qubit-based device." <https://docs.pennylane.ai/en/stable/code/api/pennylane.devices.default_qubit.html>`_
* - ``MQTSim()``
- `"A quantum circuit simulator based on decision diagrams written in C++." <https://mqt.readthedocs.io/projects/ddsim/en/latest/>`_
* - ``PennylaneRigettiSim()``
- `Simulator for the Pennylane-Rigetti plugin <https://docs.pennylane.ai/projects/rigetti/en/latest/code.html>`_
* - ``PyTketStimSim()``
- `"Stim is a fast simulator for quantum stabilizer circuits." <https://github.com/quantumlib/stim>`_
* - ``QulacsSim()``
- `"Qulacs is a fast quantum circuit simulator for simulating large, noisy, or parametric quantum circuits." <https://docs.qulacs.org/en/latest/>`_
* - ``QSimCirq()``
- `"qsim is a Schrödinger full state-vector simulator." <https://github.com/quantumlib/qsim/tree/master>`_
* - ``QiboSim()``
- `The simulator of the Qibo framework <https://qibo.science/qibo/stable/index.html>`_



To utilize these simulators you can import the corresponding backend in Python

>>> from qrisp import QuantumFloat
>>> a = QuantumFloat(3)
>>> a[:] = 3
>>> b = QuantumFloat(3)
>>> b[:] = 4
>>> c = a*b
>>> from qrisp.interface import MQTSim
>>> c.get_measurement(backend = MQTSim())
{12: 1.0}
2 changes: 1 addition & 1 deletion documentation/source/reference/Backend Interface/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ Backend Interface

BackendServer
BackendClient
DockerBackend/index
VirtualBackend
VirtualQiskitBackend
DockerSimulators

The backend interface contains a minimal set of features that apply to every gate-based quantum computer.
The main motivation in designing the interface, is to provide a convenient setup for both clients and providers of physical quantum
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = qrisp
version = 0.4.1
version = 0.4.3
author = Raphael Seidel
author_email = [email protected]
description = A high-level quantum programming language
Expand Down
4 changes: 2 additions & 2 deletions src/qrisp/arithmetic/adders/adder_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def ammended_adder(qf2, qf1, *args, ignore_rounding_error = False, ignore_overfl
elif isinstance(qf2, (list, QuantumVariable)):

if len(qf2) < len(qf1):
ancilla_var = QuantumVariable(len(qf1)-len(qf2))
ancilla_var = QuantumVariable(len(qf1)-len(qf2), name = "add_ammend_anc*", qs = qf1[0].qs())
qf2 = list(qf2) + list(ancilla_var)

raw_inpl_adder(qf2, qf1, *args, **kwargs)
Expand All @@ -173,7 +173,7 @@ def ammended_adder(qf2, qf1, *args, ignore_rounding_error = False, ignore_overfl

elif isinstance(qf2, int) and ammend_cl_int:

ancilla_var = QuantumFloat(len(qf1))
ancilla_var = QuantumFloat(len(qf1), name = "add_ammend_anc*", qs = qf1[0].qs())

ancilla_var.encode(qf2 % 2**len(qf1))

Expand Down
221 changes: 121 additions & 100 deletions src/qrisp/arithmetic/adders/gidney/cq_gidney_adder.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
# For the quantum-quantum version please check qrisp.arithmetic.adders.gidney_adder


from qrisp import bin_rep, x, cx, mcx, QuantumVariable, invert
from qrisp import bin_rep, x, cx, mcx, QuantumVariable, invert, fast_append, merge, QuantumBool

# This function inverts a bit represented by a character
def c_inv(c):
Expand All @@ -38,134 +38,155 @@ def c_inv(c):

def cq_gidney_adder(a, b, c_in = None, c_out = None, ctrl = None):

if isinstance(a, int):
a = bin_rep(a%2**len(b), len(b))[::-1]
elif not isinstance(a, str):
raise Exception(f"Tried to call semi-classical carry calculator with invalid type {type(a)}")
# Call merge to make the QuantumSession enter the inversion environment
# (doesn't happen automatically with fast_append = 3)
merge(b[0].qs())

if len(a) != len(b):
raise Exception("Tried to call Gidney adder with inputs of unequal length")

if c_out is not None:
b = list(b) + [c_out]
a = a + "0"

if c_in is not None:
b = [c_in] + list(b)
a = "1" + a

#Treat the case that b is only a single qubit
if len(b) == 1:
if a[0] == "1":
if ctrl is None:
x(b[0])
else:
cx(ctrl, b[0])
return

gidney_anc = QuantumVariable(len(b) - 1)
with fast_append(3):

# This loop now perform the iterations that are required to build up the circuit described
# in the paper
for i in range(len(b)-1):

# The relevant observation to turn Gidney's adder into a semi-classical adder
# is that the "a_i" qubit (in his paper these qubits are called "i")
# is only involved in two interactions:
# 1. The CNOT gate from the ancilla qubit of the previous iteration
# 2. The the quasi-toffoli gate into the ancilla of the current interaction
# If we consider "a_i" as a classically known value, we can recover the quantum
# value of the first step by applying an x gate onto the ancilla of the previous
# iteration if a_i is True.
if isinstance(a, int):
a = bin_rep(a%2**len(b), len(b))[::-1]
elif not isinstance(a, str):
raise Exception(f"Tried to call semi-classical carry calculator with invalid type {type(a)}")

# To "simulate" the quasi-toffoli we then simply use this ancilla as a control
# value instead.

# Combining these steps results in a quasi-toffoli with a modified control-state
# If a is True, the quasi-toffoli receives the flipped value of the ancilla
if i != 0:
if len(a) != len(b):
raise Exception("Tried to call Gidney adder with inputs of unequal length")

if c_out is not None:

if ctrl is not None:
if "1" == a[i]:
cx(ctrl, gidney_anc[i-1])
mcx([gidney_anc[i-1], b[i]], gidney_anc[i], method = "gidney", ctrl_state = "11")
if "1" == a[i]:
cx(ctrl, gidney_anc[i-1])
else:
mcx([gidney_anc[i-1], b[i]], gidney_anc[i], method = "gidney", ctrl_state = c_inv(a[i]) + "1")

cx(gidney_anc[i-1], gidney_anc[i])
# Convert to qubit if neccessary
if isinstance(c_out, QuantumBool):
c_out = c_out[0]

else:
# For the first iteration, we can simply execute a CNOT gate if a_0 is True
if a[i] == "1":
b = list(b) + [c_out]
a = a + "0"

if c_in is not None:

# Convert to qubit if neccessary
if isinstance(c_in, QuantumBool):
c_in = c_in[0]

b = [c_in] + list(b)
a = "1" + a

#Treat the case that b is only a single qubit
if len(b) == 1:
if a[0] == "1":
if ctrl is None:
cx(b[i], gidney_anc[i])
x(b[0])
else:
mcx([ctrl, b[i]], gidney_anc[i], method = "gidney")
cx(ctrl, b[0])
return

gidney_anc = QuantumVariable(len(b) - 1, name = "gidney_anc*", qs = b[0].qs())

# To realize the controlled version of this adder, note that the V-shaped
# part of the circuit in the paper is consisting of two branches that are
# almost inverse to each other.

# The one thing that stops the from being truly inverse is the CNOT gate
# from the ancilla into b (in his paper "t").

# That is: If we only control this gate (and the "tip"), the two branches will be their
# perfect inverse and the whole computation will result in the identity
# if the control qubit is in the |0> state.
if i != len(b) -2:
cx(gidney_anc[i], b[i+1])

# This snippet makes sure that the "tip" of the V shaped circuit is also
# controlled
cx(gidney_anc[-1], b[-1])

# We now perform the right branch of the V shaped circuit

with invert():
# This loop now perform the iterations that are required to build up the circuit described
# in the paper
for i in range(len(b)-1):

# Regarding the treatment of the classical values, a similar logic as
# above is applied
# The relevant observation to turn Gidney's adder into a semi-classical adder
# is that the "a_i" qubit (in his paper these qubits are called "i")
# is only involved in two interactions:
# 1. The CNOT gate from the ancilla qubit of the previous iteration
# 2. The the quasi-toffoli gate into the ancilla of the current interaction
# If we consider "a_i" as a classically known value, we can recover the quantum
# value of the first step by applying an x gate onto the ancilla of the previous
# iteration if a_i is True.

# To "simulate" the quasi-toffoli we then simply use this ancilla as a control
# value instead.

# Combining these steps results in a quasi-toffoli with a modified control-state
# If a is True, the quasi-toffoli receives the flipped value of the ancilla
if i != 0:

if ctrl is not None:

if "1" == a[i]:
cx(ctrl, gidney_anc[i-1])
mcx([gidney_anc[i-1], b[i]], gidney_anc[i], method = "gidney", ctrl_state = "11")
if "1" == a[i]:
cx(ctrl, gidney_anc[i-1])

else:
mcx([gidney_anc[i-1], b[i]], gidney_anc[i], method = "gidney", ctrl_state = c_inv(a[i]) + "1")

cx(gidney_anc[i-1], gidney_anc[i])

else:
# For the first iteration, we can simply execute a CNOT gate if a_0 is True
if a[i] == "1":
if ctrl is None:
cx(b[i], gidney_anc[i])
else:
mcx([ctrl, b[i]], gidney_anc[i], method = "gidney")


# To realize the controlled version of this adder, note that the V-shaped
# part of the circuit in the paper is consisting of two branches that are
# almost inverse to each other.

# The one thing that stops the from being truly inverse is the CNOT gate
# from the ancilla into b (in his paper "t").

# That is: If we only control this gate (and the "tip"), the two branches will be their
# perfect inverse and the whole computation will result in the identity
# if the control qubit is in the |0> state.
if i != len(b) -2:
cx(gidney_anc[i], b[i+1])

# Finally, the CNOT gates on the right side of the V shape are executed.
# This snippet makes sure that the "tip" of the V shaped circuit is also
# controlled
cx(gidney_anc[-1], b[-1])

for i in range(len(a)):
# Since the values of a (in his paper "i") are classically known we
# can use a classical condition
if a[i] == "1":
# We now perform the right branch of the V shaped circuit

with invert():

if c_in is not None and i == 0:
continue
# Here we need to again treat the control qubit, because in the
# controlled case, the V shaped part of the circuit might be properly
# controlled because of the above construction but the right most
# CNOT gates are not.
if ctrl is None:
x(b[i])
else:
cx(ctrl, b[i])

gidney_anc.delete(verify = False)
# Call merge to make the QuantumSession enter the inversion environment
# (doesn't happen automatically with fast_append = 3)
merge(b[0].qs())

for i in range(len(b)-1):

# Regarding the treatment of the classical values, a similar logic as
# above is applied
if i != 0:
if ctrl is not None:

if "1" == a[i]:
cx(ctrl, gidney_anc[i-1])
mcx([gidney_anc[i-1], b[i]], gidney_anc[i], method = "gidney", ctrl_state = "11")
if "1" == a[i]:
cx(ctrl, gidney_anc[i-1])

else:
mcx([gidney_anc[i-1], b[i]], gidney_anc[i], method = "gidney", ctrl_state = c_inv(a[i]) + "1")

cx(gidney_anc[i-1], gidney_anc[i])

else:
if a[i] == "1":
if ctrl is None:
cx(b[i], gidney_anc[i])
else:
mcx([ctrl, b[i]], gidney_anc[i], method = "gidney")

# Finally, the CNOT gates on the right side of the V shape are executed.

for i in range(len(a)):
# Since the values of a (in his paper "i") are classically known we
# can use a classical condition
if a[i] == "1":

if c_in is not None and i == 0:
continue
# Here we need to again treat the control qubit, because in the
# controlled case, the V shaped part of the circuit might be properly
# controlled because of the above construction but the right most
# CNOT gates are not.
if ctrl is None:
x(b[i])
else:
cx(ctrl, b[i])

gidney_anc.delete(verify = False)
Loading

0 comments on commit 3bb374c

Please sign in to comment.