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.
import logging
import os
from getpass import getpass
from projectq import MainEngine
from projectq.setups import linear
from projectq.ops import H, Rx, Rz, CNOT, CZ, Measure, All
from quantuminspire.credentials import load_account, get_token_authentication, get_basic_authentication
from quantuminspire.api import QuantumInspireAPI
from quantuminspire.projectq.backend_qx import QIBackend
QI_EMAIL = os.getenv('QI_EMAIL')
QI_PASSWORD = os.getenv('QI_PASSWORD')
QI_URL = os.getenv('API_URL', 'https://api.quantum-inspire.com/')
token = load_account()
if token is not None:
authentication = get_token_authentication(token)
else:
if QI_EMAIL is None or QI_PASSWORD is None:
print('Enter email')
email = input()
print('Enter password')
password = getpass()
else:
email, password = QI_EMAIL, QI_PASSWORD
authentication = get_basic_authentication(email, password)
qi_api = QuantumInspireAPI(QI_URL, authentication)
projectq_backend = QIBackend(quantum_inspire_api=qi_api)
We create an algorithm to entangle qubit 0 and qubit 4.
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.5146484375, '10001': 0.4853515625} 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.
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.
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: {'10001': 0.505859375, '00000': 0.494140625} 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.
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