Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci_backends.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
# myqlm does not work in python3.7
pip install "cirq" "qiskit>=0.30" "qulacs" "qibo==0.1.1"
elif [ $ver -eq 8 ]; then
pip install "cirq" "qiskit>=0.30" "qulacs" "qibo==0.1.1" "myqlm" "cirq-google"
pip install "cirq" "qiskit" "qulacs" "qibo==0.1.1" "myqlm" "cirq-google"
fi
pip install -e .
- name: Lint with flake8
Expand Down
7 changes: 1 addition & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,12 +342,7 @@ You can avoid it by downgrading cirq and openfermion
```bash
pip install --upgrade "openfermion<=1.0.0"
pip install --upgrade "cirq<=0.9.1"
```


## Qiskit backend
Qiskit version 0.25 is not yet supported.
`pip install --upgrade qiskit<0.25` fixes potential issues. If not: Please let us know.
```

## Circuit drawing
Standard graphical circuit representation within a Jupyter environment is often done using `tq.draw`.
Expand Down
21 changes: 17 additions & 4 deletions src/tequila/simulators/simulator_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
from tequila.simulators.simulator_base import BackendCircuit, BackendExpectationValue
from tequila.circuit.noise import NoiseModel

SUPPORTED_BACKENDS = ["qulacs_gpu", "qulacs",'qibo', "qiskit", "cirq", "pyquil", "symbolic", "qlm"]
SUPPORTED_NOISE_BACKENDS = ["qiskit", 'cirq', 'pyquil'] # qulacs removed in v.1.9
SUPPORTED_BACKENDS = ["qulacs", "qulacs_gpu", "qibo", "qiskit", "qiskit_gpu", "cirq", "pyquil", "symbolic", "qlm"]
SUPPORTED_NOISE_BACKENDS = ["qiskit", "qiskit_gpu", "cirq", "pyquil"] # qulacs removed in v.1.9
BackendTypes = namedtuple('BackendTypes', 'CircType ExpValueType')
INSTALLED_SIMULATORS = {}
INSTALLED_SAMPLERS = {}
Expand Down Expand Up @@ -43,6 +43,19 @@
HAS_QISKIT = False
HAS_QISKIT_NOISE = False

try:
pkg_resources.require("qiskit-aer-gpu")
from tequila.simulators.simulator_qiskit_gpu import BackendCircuitQiskitGpu, BackendExpectationValueQiskitGpu
HAS_QISKIT_GPU = True
INSTALLED_SIMULATORS["qiskit_gpu"] = BackendTypes(BackendCircuitQiskitGpu, BackendExpectationValueQiskitGpu)
INSTALLED_SAMPLERS["qiskit_gpu"] = BackendTypes(BackendCircuitQiskitGpu, BackendExpectationValueQiskitGpu)
from tequila.simulators.simulator_qiskit_gpu import HAS_NOISE as HAS_QISKIT_GPU_NOISE
if HAS_QISKIT_GPU_NOISE:
INSTALLED_NOISE_SAMPLERS["qiskit_gpu"] = BackendTypes(BackendCircuitQiskitGpu, BackendExpectationValueQiskitGpu)
except (ImportError, DistributionNotFound):
HAS_QISKIT_GPU = False
HAS_QISKIT_GPU_NOISE = False

HAS_QIBO = True
try:
from tequila.simulators.simulator_qibo import BackendCircuitQibo, BackendExpectationValueQibo
Expand Down Expand Up @@ -82,8 +95,8 @@
HAS_QULACS = False

try:
pkg_resources.require("qulacs-gpu")
import qulacs
# pkg_resources.require("qulacs-gpu")
from qulacs import QuantumStateGpu
from tequila.simulators.simulator_qulacs_gpu import BackendCircuitQulacsGpu, BackendExpectationValueQulacsGpu

HAS_QULACS_GPU = True
Expand Down
103 changes: 62 additions & 41 deletions src/tequila/simulators/simulator_qiskit.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
from tequila.simulators.simulator_base import BackendCircuit, QCircuit, BackendExpectationValue
from tequila.utils.bitstrings import reverse_int_bits
from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
from tequila import TequilaException, TequilaWarning
from tequila import BitString, BitNumbering, BitStringLSB
from tequila.utils.keymap import KeyMapRegisterToSubregister
from tequila.utils import to_float
import qiskit, numpy, warnings
import warnings
import numpy as np
import qiskit, qiskit_aer, qiskit.providers.fake_provider

HAS_NOISE=True
HAS_NOISE = True
try:
from qiskit_aer import noise as qiskitnoise
from qiskit_aer import noise as qiskitnoise, AerSimulator
except:
HAS_NOISE = False

HAS_IBMQ=True
HAS_IBMQ = True
try:
from qiskit.providers.ibmq import IBMQBackend
from qiskit_ibm_runtime import IBMBackend
except:
HAS_IBMQ=False
HAS_IBMQ = False


def get_bit_flip(p):
"""
Expand Down Expand Up @@ -70,16 +74,24 @@ def get_phase_flip(p):
'multicontrol': 3
}

full_basis = ['x', 'y', 'z', 'id', 'u1', 'u2', 'u3', 'h','unitary','sx',
full_basis = ['x', 'y', 'z', 'id', 'u1', 'u2', 'u3', 'h', 'unitary', 'sx',
'cx', 'cy', 'cz', 'cu3', 'ccx']


def qiskit_device_dict():
devices = {}
devices.update({str(x).lower():x for x in qiskit.Aer.backends()})
devices.update({str(x).lower():x for x in qiskit.test.mock.FakeProvider().backends()})
# As of Quiskit Aer 0.15.0, backends() also returns legacy backends which are not AerSimulators that will be
# deprecated in the future
# TODO: Instead of building a dict with all backends just to search it, search directly with `get_backend`
# https://qiskit.github.io/qiskit-aer/stubs/qiskit_aer.AerProvider.html#qiskit_aer.AerProvider.get_backend
devices = {backend.name.lower(): backend for backend in qiskit_aer.AerProvider().backends()
if isinstance(backend, AerSimulator)}

# FakeProvider has been removed, see https://github.com/Qiskit/qiskit/issues/10954
# devices.update({str(x).lower(): x for x in qiskit.test.mock.FakeProvider().backends()})

return devices


class TequilaQiskitException(TequilaException):
def __str__(self):
return "Error in qiskit backend:" + self.message
Expand Down Expand Up @@ -125,6 +137,8 @@ class BackendCircuitQiskit(BackendCircuit):
transform a tequila NoiseModel into a qiskit noise model.

"""
STATEVECTOR_DEVICE_NAME = "aer_simulator_statevector"

compiler_arguments = {
"trotterized": True,
"swap": False,
Expand All @@ -145,7 +159,7 @@ class BackendCircuitQiskit(BackendCircuit):
}

numbering = BitNumbering.LSB

def __init__(self, abstract_circuit: QCircuit, variables, qubit_map=None, noise=None,
device=None, *args, **kwargs):
"""
Expand Down Expand Up @@ -188,8 +202,9 @@ def __init__(self, abstract_circuit: QCircuit, variables, qubit_map=None, noise=
if qubit_map is None:
qubit_map = {q: i for i, q in enumerate(abstract_circuit.qubits)}
else:
warnings.warn("reveived custom qubit_map = {}\n"
"This is not fully integrated with qiskit and might result in unexpected behaviour".format(qubit_map), TequilaWarning)
warnings.warn(f"reveived custom qubit_map = {qubit_map}\n"
"This is not fully integrated with qiskit and might result in unexpected behaviour",
TequilaWarning)

n_qubits = max(qubit_map.values()) + 1

Expand All @@ -201,7 +216,7 @@ def __init__(self, abstract_circuit: QCircuit, variables, qubit_map=None, noise=

self.classical_map = self.make_classical_map(qubit_map=self.qubit_map)

if noise != None:
if noise is not None:
self.noise_lookup = {
'phase damp': qiskitnoise.phase_damping_error,
'amplitude damp': qiskitnoise.amplitude_damping_error,
Expand All @@ -211,12 +226,12 @@ def __init__(self, abstract_circuit: QCircuit, variables, qubit_map=None, noise=
'depolarizing': qiskitnoise.depolarizing_error
}

if isinstance(noise, str): #string noise means "use the same noise as the device I tell you to get."
if isinstance(noise, str): # string noise means "use the same noise as the device I tell you to get."
try:
self.check_device(noise)
self.noise_model = qiskitnoise.NoiseModel.from_backend(noise)
except TequilaQiskitException:
raise TequilaException("noise init from string requires that noise names a device. Got {}".format(noise))
raise TequilaException(f"noise init from string requires that noise names a device. Got {noise}")

else:
self.noise_model = self.noise_model_converter(noise)
Expand All @@ -235,7 +250,7 @@ def make_qubit_map(self, qubits: dict = None):
qubit_map = super().make_qubit_map(qubits=qubits)
mapped_qubits = [q.number for q in qubit_map.values()]
for k, v in qubit_map.items():
qubit_map[k].instance = self.q [v.number]
qubit_map[k].instance = self.q[v.number]

return qubit_map

Expand Down Expand Up @@ -266,10 +281,12 @@ def do_simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveF
"""
if self.noise_model is None:
if self.device is None:
qiskit_backend = self.retrieve_device('statevector_simulator')
qiskit_backend = self.retrieve_device(self.STATEVECTOR_DEVICE_NAME)
else:
if 'statevector' not in str(self.device):
raise TequilaException('For simulation, only state vector simulators are supported; recieved device={}, you might have forgoten to set the samples keyword - e.g. (device={}, samples=1000). If not set, tequila assumes that full wavefunction simualtion is demanded which is not compatible with qiskit devices or fake devices except for device=statevector'.format(self.device, self.device))
raise TequilaException(
'For simulation, only state vector simulators are supported; recieved device={}, you might have forgoten to set the samples keyword - e.g. (device={}, samples=1000). If not set, tequila assumes that full wavefunction simualtion is demanded which is not compatible with qiskit devices or fake devices except for device=statevector'.format(
self.device, self.device))
else:
qiskit_backend = self.retrieve_device(self.device)
else:
Expand All @@ -279,22 +296,24 @@ def do_simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveF
if "optimization_level" in kwargs:
optimization_level = kwargs['optimization_level']

opts = {}
circuit = self.circuit.assign_parameters(self.resolver)

if initial_state != 0:
array = numpy.zeros(shape=[2 ** self.n_qubits])
i = BitStringLSB.from_binary(BitString.from_int(integer=initial_state, nbits=self.n_qubits).binary)
print(initial_state, " -> ", i)
array[i.integer] = 1.0
opts = {"initial_statevector": array}
state = np.zeros(2 ** self.n_qubits)
initial_state = reverse_int_bits(initial_state, self.n_qubits)
state[initial_state] = 1.0
init_circuit = qiskit.QuantumCircuit(self.q, self.c)
init_circuit.set_statevector(state)
circuit = init_circuit.compose(circuit)

circuit = self.circuit.bind_parameters(self.resolver)
circuit.save_statevector()

qiskit_job = qiskit_backend.run(circuit,optimization_level=optimization_level,**opts)
backend_result = qiskit_backend.run(circuit, optimization_level=optimization_level).result()

backend_result = qiskit_job.result()
return QubitWaveFunction.from_array(arr=backend_result.get_statevector(circuit), numbering=self.numbering)

def do_sample(self, circuit: qiskit.QuantumCircuit, samples: int, read_out_qubits, *args, **kwargs) -> QubitWaveFunction:
def do_sample(self, circuit: qiskit.QuantumCircuit, samples: int, read_out_qubits, *args,
**kwargs) -> QubitWaveFunction:
"""
Helper function for performing sampling.
Parameters
Expand All @@ -319,17 +338,18 @@ def do_sample(self, circuit: qiskit.QuantumCircuit, samples: int, read_out_qubit
else:
qiskit_backend = self.retrieve_device(self.device)

if isinstance(qiskit_backend,IBMQBackend):
if isinstance(qiskit_backend, IBMBackend):
if self.noise_model is not None:
raise TequilaException('Cannot combine backend {} with custom noise models.'.format(str(qiskit_backend)))
circuit = circuit.bind_parameters(self.resolver) # this is necessary in spite of qiskit "fixing" it
raise TequilaException(
'Cannot combine backend {} with custom noise models.'.format(str(qiskit_backend)))
circuit = circuit.assign_parameters(self.resolver) # this is necessary in spite of qiskit "fixing" it
circuit = qiskit.transpile(circuit, qiskit_backend)
return self.convert_measurements(qiskit_backend.run(circuit,shots=samples,
optimization_level=optimization_level),
return self.convert_measurements(qiskit_backend.run(circuit, shots=samples,
optimization_level=optimization_level),
target_qubits=read_out_qubits)
else:
if isinstance(qiskit_backend, qiskit.test.mock.FakeBackend):
circuit = circuit.bind_parameters(self.resolver) # this is necessary in spite of qiskit "fixing" it
if isinstance(qiskit_backend, qiskit.providers.fake_provider.FakeBackend):
circuit = circuit.assign_parameters(self.resolver) # this is necessary in spite of qiskit "fixing" it
coupling_map = qiskit_backend.configuration().coupling_map
from_back = qiskitnoise.NoiseModel.from_backend(qiskit_backend)
if self.noise_model is not None:
Expand All @@ -343,15 +363,15 @@ def do_sample(self, circuit: qiskit.QuantumCircuit, samples: int, read_out_qubit
optimization_level=optimization_level
)

job=qiskit_backend.run(circuit, shots=samples)
return self.convert_measurements(job,target_qubits=read_out_qubits)
job = qiskit_backend.run(circuit, shots=samples)
return self.convert_measurements(job, target_qubits=read_out_qubits)
else:
if self.noise_model is not None:
qiskit_backend.set_options(noise_model=self.noise_model) # fits better with our methodology.
use_basis = full_basis
else:
use_basis = qiskit_backend.configuration().basis_gates
circuit = circuit.bind_parameters(self.resolver) # this is necessary -- see qiskit-aer issue 1346
circuit = circuit.assign_parameters(self.resolver) # this is necessary -- see qiskit-aer issue 1346
circuit = qiskit.transpile(circuit, backend=qiskit_backend,
basis_gates=use_basis,
optimization_level=optimization_level
Expand Down Expand Up @@ -436,7 +456,8 @@ def add_parametrized_gate(self, gate, circuit, *args, **kwargs):
par = float(gate.parameter)
if gate.is_controlled():
if len(gate.control) > 2:
raise TequilaQiskitException("multi-controls beyond 2 not yet supported for the qiskit backend. Gate was:\n{}".format(gate) )
raise TequilaQiskitException(
"multi-controls beyond 2 not yet supported for the qiskit backend. Gate was:\n{}".format(gate))
ops[1](circuit)(par, self.qubit(gate.control[0]), self.qubit(gate.target[0]))
else:
ops[0](circuit)(par, self.qubit(gate.target[0]))
Expand Down Expand Up @@ -590,7 +611,7 @@ def check_device(self, device):
if device is None:
return

elif isinstance(device,qiskit.providers.Backend):
elif isinstance(device, qiskit.providers.Backend):
return

elif isinstance(device, dict):
Expand Down
9 changes: 9 additions & 0 deletions src/tequila/simulators/simulator_qiskit_gpu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from tequila.simulators.simulator_qiskit import BackendCircuitQiskit, BackendExpectationValueQiskit


class BackendCircuitQiskitGpu(BackendCircuitQiskit):
STATEVECTOR_DEVICE_NAME = "aer_simulator_statevector_gpu"


class BackendExpectationValueQiskitGpu(BackendExpectationValueQiskit):
BackendCircuitType = BackendCircuitQiskitGpu
4 changes: 2 additions & 2 deletions src/tequila/utils/bitstrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def numbering(self) -> BitNumbering:
return BitNumbering.LSB


def _reverse_int_bits(x: int, nbits: int) -> int:
def reverse_int_bits(x: int, nbits: int) -> int:
if nbits is None:
nbits = x.bit_length()
assert nbits <= 32
Expand All @@ -193,7 +193,7 @@ def _reverse_int_bits(x: int, nbits: int) -> int:

def initialize_bitstring(integer: int, nbits: int = None, numbering_in: BitNumbering = BitNumbering.MSB,
numbering_out: BitNumbering = BitNumbering.MSB):
integer = _reverse_int_bits(integer, nbits) if numbering_in != numbering_out else integer
integer = reverse_int_bits(integer, nbits) if numbering_in != numbering_out else integer
if numbering_out == BitNumbering.MSB:
return BitString.from_int(integer=integer, nbits=nbits)
else:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_simulator_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ def test_wfn_multitarget(simulator):
@pytest.mark.parametrize("simulator", tequila.simulators.simulator_api.INSTALLED_SIMULATORS.keys())
def test_wfn_multi_control(simulator):
# currently no compiler, so that test can not succeed
if simulator == 'qiskit':
if simulator in ["qiskit", "qiskit_gpu"]:
return
ac = tq.gates.X([0, 1, 2])
ac += tq.gates.Ry(target=[0], control=[1, 2], angle=2.3 / 2)
Expand Down