Example to use ProjectQ to run algorithms on Quantum Inspire

Copyright 2018 QuTech Delft. Licensed under the Apache License, Version 2.0.

For more information on Quantum Inspire, see https://www.quantum-inspire.com/. For more information on ProjectQ, see https://github.com/ProjectQ-Framework/ProjectQ.

[1]:
import os

from projectq import MainEngine
from projectq.setups import linear
from projectq.ops import H, Rx, Rz, CNOT, Measure, All

from quantuminspire.api import QuantumInspireAPI
from quantuminspire.credentials import get_authentication
from quantuminspire.projectq.backend_qx import QIBackend

QI_URL = os.getenv('API_URL', 'https://api.quantum-inspire.com/')
[2]:
authentication = get_authentication()
qi_api = QuantumInspireAPI(QI_URL, authentication)

projectq_backend = QIBackend(quantum_inspire_api=qi_api)

Execute algorithm on QX simulator

We create an algorithm to entangle qubit 0 and qubit 4.

[3]:
engine = MainEngine(backend=projectq_backend)  # create default compiler (simulator back-end)

qubits = engine.allocate_qureg(5)
q1 = qubits[0]
q2 = qubits[-1]

H | q1  # apply a Hadamard gate
CNOT | (q1, q2)
All(Measure) | qubits  # measure the qubits

engine.flush()  # flush all gates (and execute measurements)

print("Measured {}".format(','.join([str(int(q)) for q in qubits])))
print('Probabilities: %s' % (projectq_backend.get_probabilities(qubits),))
print(projectq_backend.cqasm())
Measured 1,0,0,0,1
Probabilities: {'00000': 0.4921875, '10001': 0.5078125}
version 1.0
# cQASM generated by Quantum Inspire <class 'quantuminspire.projectq.backend_qx.QIBackend'> class
qubits 5

h q[0]
cnot q[0], q[4]

The result is as expected: about half of the results is split between 0 and 1 on qubit 0 and 4. The QASM generated by the backend is fairly simple.

Simulate a spin-qubit array

On a spin-qubit array we have limited connectivity and also a limited set of gates available. With ProjectQ we can handle these cases by adding specific compiler engines. Our engine lists is generated by the projectq.setups.linear module.

[4]:
projectq_backend = QIBackend(quantum_inspire_api=qi_api)
engine_list = linear.get_engine_list(num_qubits=5, one_qubit_gates=(Rx, Rz), two_qubit_gates=(CNOT,))
engine = MainEngine(backend=projectq_backend, engine_list=engine_list)  # create default compiler (simulator back-end)

qubits = engine.allocate_qureg(5)
q1 = qubits[0]
q2 = qubits[-1]

H | q1  # apply a Hadamard gate
CNOT | (q1, q2)
All(Measure) | qubits  # measure the qubits

engine.flush()  # flush all gates (and execute measurements)

print("Measured {}".format(','.join([str(int(q)) for q in qubits])))
print('Probabilities: %s' % (projectq_backend.get_probabilities(qubits),))
print(projectq_backend.cqasm())
Measured 1,0,0,0,1
Probabilities: {'00000': 0.5048828125, '10001': 0.4951171875}
version 1.0
# cQASM generated by Quantum Inspire <class 'quantuminspire.projectq.backend_qx.QIBackend'> class
qubits 5

rx q[0],1.5707963268
rz q[0],1.5707963268
rx q[0],7.85398163397
cnot q[0], q[1]

The result is the same, but if we look at the QASM generated there is quite a difference. The H gate was replaced by some single qubit operations. Also the qubits 0 and 4 have been mapped to neighboring qubits.

[5]:
current_mapping = engine.mapper.current_mapping
for l, p in current_mapping.items():
    print('mapping logical qubit %d to physical qubit %d' % (l, p))
mapping logical qubit 0 to physical qubit 0
mapping logical qubit 4 to physical qubit 1
mapping logical qubit 1 to physical qubit 2
mapping logical qubit 2 to physical qubit 3
mapping logical qubit 3 to physical qubit 4
[ ]: