Skip to content

Commit

Permalink
Merge pull request #368 from ohuettenhofer/fix_qiskit
Browse files Browse the repository at this point in the history
* Fix current Qiskit versions

* Update ci_backends.yml
kottmanj authored Nov 1, 2024
2 parents 9d860ff + e9a385f commit c746b0f
Showing 4 changed files with 66 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci_backends.yml
Original file line number Diff line number Diff line change
@@ -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
7 changes: 1 addition & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -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`.
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):
"""
@@ -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
@@ -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,
@@ -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):
"""
@@ -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

@@ -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,
@@ -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)
@@ -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

@@ -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:
@@ -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
@@ -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:
@@ -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
@@ -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]))
@@ -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):
4 changes: 2 additions & 2 deletions src/tequila/utils/bitstrings.py
Original file line number Diff line number Diff line change
@@ -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
@@ -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:

0 comments on commit c746b0f

Please sign in to comment.