Ich mache Blueqat, eine Quantencomputer-Bibliothek, und ich plane, den Quellcode von Qiskit, einer Quantencomputer-Bibliothek, zu lesen.
In Fortsetzung von Letztes Mal lesen wir diesmal den Prozess des Lesens der Backend-Erfassung, des Aufrufs und des Erhaltens von Ergebnissen. Anstatt die Quelle des Simulators selbst zu lesen, werden wir uns mit den Schnittstellen rund um das Backend befassen.
Qiskit ist eine von IBM entwickelte Open-Source-Quantencomputerbibliothek.
Qiskit ist wie unten gezeigt in Pakete unterteilt, aber bei der Installation ist es weniger mühsam, es gemeinsam mit "pip install qiskit" zu installieren, als es separat zu tun.
Paket | Rolle |
---|---|
Qiskit Terra | Dies ist das Hauptpaket. Es enthält eine Klasse zum Erstellen einer Schaltung, eine Funktion zum Transpilieren der Schaltung für die tatsächliche Maschine, eine Funktion zum Aufrufen der API und zum Werfen auf die tatsächliche Maschine usw. |
Qiskit Aer | Enthält einen Quantenschaltungssimulator, der normalerweise von Qiskit Terra aufgerufen wird |
Qiskit Ignis | Dies ist eine Bibliothek für diejenigen, die das Rauschen bekämpfen möchten, wenn sie eine Quantenschaltung auf einer tatsächlichen Maschine betreiben. Ich habe noch nie benutzt |
Qiskit Aqua | Eine Bibliothek, die die Verwendung von Quantenalgorithmen vereinfacht |
Diesmal lese ich einen Teil von Qiskit Terra.
https://github.com/Qiskit/qiskit-terra
Insbesondere der in README.md geschriebene Code
from qiskit import *
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0,1], [0,1])
backend_sim = BasicAer.get_backend('qasm_simulator')
result = execute(qc, backend_sim).result()
print(result.get_counts(qc))
Lesen Sie dazu den Ablauf nach "backend_sim = BasicAer.get_backend (" qasm_simulator ")". Ich werde jedoch die Details des Simulatorinhalts oder der Transpile nicht lesen.
Lesen Sie weiter den Hauptzweig auf GitHub. Die aktuelle Commit-ID lautet e7be587, wird jedoch häufig aktualisiert und kann sich bis zum Ende des Artikels ändern. Bitte beachten Sie.
backend_sim = BasicAer.get_backend('qasm_simulator')
Lesen Sie diese Zeile.
BasicAer
befindet sich in terras qiskit / provider / basicaer / init.py
BasicAer = BasicAerProvider()
Ist definiert als.
BasicAerProvider
Lesen Sie den "BasicAerProvider" in qiskit / provider / basicaer / basicaerprovider.py.
SIMULATORS = [
QasmSimulatorPy,
StatevectorSimulatorPy,
UnitarySimulatorPy
]
class BasicAerProvider(BaseProvider):
"""Provider for Basic Aer backends."""
def __init__(self, *args, **kwargs):
super().__init__(args, kwargs)
# Populate the list of Basic Aer backends.
self._backends = self._verify_backends()
#Abkürzung
def _verify_backends(self):
"""
Return the Basic Aer backends in `BACKENDS` that are
effectively available (as some of them might depend on the presence
of an optional dependency or on the existence of a binary).
Returns:
dict[str:BaseBackend]: a dict of Basic Aer backend instances for
the backends that could be instantiated, keyed by backend name.
"""
ret = OrderedDict()
for backend_cls in SIMULATORS:
try:
backend_instance = self._get_backend_instance(backend_cls)
backend_name = backend_instance.name()
ret[backend_name] = backend_instance
except QiskitError as err:
# Ignore backends that could not be initialized.
logger.info('Basic Aer backend %s is not available: %s',
backend_cls, str(err))
return ret
Lesen Sie vorerst das __init__
der übergeordneten Klasse BaseProvider
.
class BaseProvider(ABC):
"""Base class for a Backend Provider."""
def __init__(self, *args, **kwargs):
pass
Ich mache gar nichts.
fortsetzen
self._backends = self._verify_backends()
Lesen Sie _verify_backends
in. Ich habe den obigen Code eingefügt, aber es ist wirklich schlecht, etwas anderes zu tun, als mit dem Namen "verify" zu verifizieren ...
Stell es zur Seite. Wir können die Struktur sehen, dass "BasicAer" ein Anbieter ist und ein Anbieter ein "Backend" hat.
Es scheint, dass die in [SIMULATORS "definierten Klassen" [QasmSimulatorPy, StatevectorSimulatorPy, UnitarySimulatorPy] "jeweils" _get_backend_instance "sind und in ein (geordnetes) Wörterbuch gepackt und zurückgegeben werden.
Lesen wir "_get_backend_instance". Die Dokumentzeichenfolge wird gelöscht und in Anführungszeichen gesetzt.
def _get_backend_instance(self, backend_cls):
# Verify that the backend can be instantiated.
try:
backend_instance = backend_cls(provider=self)
except Exception as err:
raise QiskitError('Backend %s could not be instantiated: %s' %
(backend_cls, err))
return backend_instance
Ich erstelle nur eine Instanz der Backend-Klasse, übergebe aber den Anbieter selbst an die Instanz. Der Anbieter und das Backend stehen in einer gegenseitigen Referenzbeziehung.
QasmSimulatorPy .__ init__
Diese Backend-Klassen selbst
qiskit / provider / basicaer / qasm_simulator.py, qiskit / provider / basicaer / statevector_simulator. py, [qiskit / provider / basicaer / unitary_simulator.py](https: // github. Es ist in com / Qiskit / qiskit-terra / blob / master / qiskit / provider / basicaer / unitary_simulator.py definiert. Schauen wir uns einfach QasmSimulatorPy .__ init__
an.
class QasmSimulatorPy(BaseBackend):
"""Python implementation of a qasm simulator."""
MAX_QUBITS_MEMORY = int(log2(local_hardware_info()['memory'] * (1024 ** 3) / 16))
DEFAULT_CONFIGURATION = {
'backend_name': 'qasm_simulator',
'backend_version': '2.0.0',
'n_qubits': min(24, MAX_QUBITS_MEMORY),
'url': 'https://github.com/Qiskit/qiskit-terra',
'simulator': True,
'local': True,
'conditional': True,
'open_pulse': False,
'memory': True,
'max_shots': 65536,
'coupling_map': None,
'description': 'A python simulator for qasm experiments',
'basis_gates': ['u1', 'u2', 'u3', 'cx', 'id', 'unitary'],
'gates': [
{
'name': 'u1',
'parameters': ['lambda'],
'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }'
},
{
'name': 'u2',
'parameters': ['phi', 'lambda'],
'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }'
},
{
'name': 'u3',
'parameters': ['theta', 'phi', 'lambda'],
'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }'
},
{
'name': 'cx',
'parameters': ['c', 't'],
'qasm_def': 'gate cx c,t { CX c,t; }'
},
{
'name': 'id',
'parameters': ['a'],
'qasm_def': 'gate id a { U(0,0,0) a; }'
},
{
'name': 'unitary',
'parameters': ['matrix'],
'qasm_def': 'unitary(matrix) q1, q2,...'
}
]
}
DEFAULT_OPTIONS = {
"initial_statevector": None,
"chop_threshold": 1e-15
}
# Class level variable to return the final state at the end of simulation
# This should be set to True for the statevector simulator
SHOW_FINAL_STATE = False
def __init__(self, configuration=None, provider=None):
super().__init__(configuration=(
configuration or QasmBackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)),
provider=provider)
# Define attributes in __init__.
self._local_random = np.random.RandomState()
self._classical_memory = 0
self._classical_register = 0
self._statevector = 0
self._number_of_cmembits = 0
self._number_of_qubits = 0
self._shots = 0
self._memory = False
self._initial_statevector = self.DEFAULT_OPTIONS["initial_statevector"]
self._chop_threshold = self.DEFAULT_OPTIONS["chop_threshold"]
self._qobj_config = None
# TEMP
self._sample_measure = False
Ich habe das Gefühl, verschiedene Einstellungen vorgenommen zu haben. Was Sie tun, ist nur das Initialisieren von Variablen, und es sieht nicht so aus, als würden Sie viel tun. Sie können nur bis zu 24 Quantenbits simulieren. Ich wusste es nicht. (Ich habe den Eindruck, dass es ziemlich stark eingestellt ist. Sie sollten in der Lage sein, auch mit einem normalen Computer Ihr Bestes zu geben.)
Es scheint, dass das Backend selbst einen Zustand aufweist, der in Bearbeitung zu sein scheint, wie z. B. die Anzahl der Aufnahmen und das Gedächtnis. (Blueqat wagt es, eine solche Implementierung zu vermeiden) Die Idee hier wird möglicherweise etwas klarer, wenn Sie den Code lesen.
--BasicAer
ist eine Instanz von BasicAerProvider
Ich verstehe. Schauen wir uns als nächstes BasicAer.get_backend
an.
BasicAer.get_backend
def get_backend(self, name=None, **kwargs):
backends = self._backends.values()
# Special handling of the `name` parameter, to support alias resolution
# and deprecated names.
if name:
try:
resolved_name = resolve_backend_name(
name, backends,
self._deprecated_backend_names(),
{}
)
name = resolved_name
except LookupError:
raise QiskitBackendNotFoundError(
"The '{}' backend is not installed in your system.".format(name))
return super().get_backend(name=name, **kwargs)
Obwohl ich "self._backends" im Wörterbuch habe, bin ich neugierig, dass ich es mache, ohne das Wörterbuch nachzuschlagen.
Lesen wir resolve_backend_name
.
resolve_backend_name
resolve_backend_name
ist in [qiskit / provider / providerutils.py] definiert (https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/providers/providerutils.py).
def resolve_backend_name(name, backends, deprecated, aliased):
"""Resolve backend name from a deprecated name or an alias.
A group will be resolved in order of member priorities, depending on
availability.
Args:
name (str): name of backend to resolve
backends (list[BaseBackend]): list of available backends.
deprecated (dict[str: str]): dict of deprecated names.
aliased (dict[str: list[str]]): dict of aliased names.
Returns:
str: resolved name (name of an available backend)
Raises:
LookupError: if name cannot be resolved through regular available
names, nor deprecated, nor alias names.
"""
available = [backend.name() for backend in backends]
resolved_name = deprecated.get(name, aliased.get(name, name))
if isinstance(resolved_name, list):
resolved_name = next((b for b in resolved_name if b in available), "")
if resolved_name not in available:
raise LookupError("backend '{}' not found.".format(name))
if name in deprecated:
logger.warning("Backend '%s' is deprecated. Use '%s'.", name,
resolved_name)
return resolved_name
Erstellen Sie mit available
eine Liste mit Backend-Namen.
"veraltet" können diejenigen, die Qiskit seit einiger Zeit verwenden, unter "BasicAerProvider._deprecated_backend_names ()" sehen.
@staticmethod
def _deprecated_backend_names():
"""Returns deprecated backend names."""
return {
'qasm_simulator_py': 'qasm_simulator',
'statevector_simulator_py': 'statevector_simulator',
'unitary_simulator_py': 'unitary_simulator',
'local_qasm_simulator_py': 'qasm_simulator',
'local_statevector_simulator_py': 'statevector_simulator',
'local_unitary_simulator_py': 'unitary_simulator',
'local_unitary_simulator': 'unitary_simulator',
}
Es ist ein Wörterbuch von "{" alter Backend-Name ":" aktiver Backend-Name "}". Wenn der gesuchte Backend-Name "veraltet" ist, konvertieren Sie ihn in den aktiven Backend-Namen.
Für "Alias" suchte ich nach "Qiskit-Terra" und "Qiskit-Aer", konnte aber nichts finden, was nicht leer war, aber der gesuchte Backend-Name befand sich im "Alias" -Wörterbuch. Wenn ja, scheint es die Liste der Aliase abzurufen. Da die Liste mehrere Aliase enthält, machen Sie sie zum allerersten "verfügbaren" Backend-Namen. Wenn keiner von ihnen "verfügbar" ist, machen Sie ihn zu einer leeren Zeichenfolge.
Wenn die Namenskonvertierung abgeschlossen ist, überprüfen Sie das Backend, das dem konvertierten Namen entspricht, geben Sie den Namen zurück, falls vorhanden, und lösen Sie eine Ausnahme aus, wenn nicht.
get_backend ()
Nachdem Sie den Namen konvertiert und einen "verfügbaren" Namen erhalten haben,
return super().get_backend(name=name, **kwargs)
Ich habe gesagt. Lesen wir also "BaseProvider.get_backend ()". Lassen Sie die Dokumentzeichenfolge weg.
def get_backend(self, name=None, **kwargs):
backends = self.backends(name, **kwargs)
if len(backends) > 1:
raise QiskitBackendNotFoundError('More than one backend matches the criteria')
if not backends:
raise QiskitBackendNotFoundError('No backend matches the criteria')
return backends[0]
Gibt das erste Element der Liste oder etwas zurück, das von "BasicAerProvider.backends (name)" zurückgegeben wurde.
Lesen Sie BasicAerProvider.backends
.
def backends(self, name=None, filters=None, **kwargs):
# pylint: disable=arguments-differ
backends = self._backends.values()
# Special handling of the `name` parameter, to support alias resolution
# and deprecated names.
if name:
try:
resolved_name = resolve_backend_name(
name, backends,
self._deprecated_backend_names(),
{}
)
backends = [backend for backend in backends if
backend.name() == resolved_name]
except LookupError:
return []
return filter_backends(backends, filters=filters, **kwargs)
Es ruft auch resolve_backend_name
auf. Sofern das "veraltete" nicht im Umlauf ist oder so etwas Seltsames, sollten Sie das gleiche Ergebnis erzielen, egal wie oft Sie es versuchen. Dieses Mal ziehe ich das Backend selbst heraus, nicht den Namen.
Was filter_backends
betrifft, ist es eine lange Zeit, obwohl ich nicht viel getan habe. Wenn Sie also interessiert sind, qiskit / provider / providerutils.py Weitere Informationen finden Sie unter /qiskit/providers/providerutils.py).
Es werden nur Dokumentzeichenfolgen und Kommentare zitiert.
def filter_backends(backends, filters=None, **kwargs):
"""Abkürzung
Args:
backends (list[BaseBackend]): list of backends.
filters (callable): filtering conditions as a callable.
**kwargs: dict of criteria.
Returns:
list[BaseBackend]: a list of backend instances matching the
conditions.
"""
# Inspect the backends to decide which filters belong to
# backend.configuration and which ones to backend.status, as it does
# not involve querying the API.
# 1. Apply backend.configuration filtering.
# 2. Apply backend.status filtering (it involves one API call for
# each backend).
# 3. Apply acceptor filter.
kwargs
gibt Bedingungen für backend.configuration
und backend.status
an.
Wenn diese auf gemischte Weise angegeben werden, werden sie auf der Seite "filter_backends" sortiert.
Wenn Sie die Funktion dem Argument "filter" zuweisen, wird das Ergebnis der Filterung mit der in Python integrierten Funktion "filter" zurückgegeben.
Die Geschichte geht übrigens schief. Sie können None als Argument an die in Python integrierte Filterfunktion übergeben. Ich wusste es nicht.
Return an iterator yielding those items of iterable for which function(item) is true. If function is None, return the items that are true.
Es scheint.
list(filter(None, [1, 2, 0, 3, "", "a"]))
# => [1, 2, 3, 'a']
Wie auch immer, ich konnte ein Backend bekommen, das dem Namen entsprach, obwohl ich durcheinander war. mit diesem
backend_sim = BasicAer.get_backend('qasm_simulator')
Sie können die Zeile lesen.
fortsetzen
result = execute(qc, backend_sim).result()
Lesen Sie in der Zeile execute
.
Unter qiskit / execute.py wird docstring gelöscht und in Anführungszeichen gesetzt.
def execute(experiments, backend,
basis_gates=None, coupling_map=None, # circuit transpile options
backend_properties=None, initial_layout=None,
seed_transpiler=None, optimization_level=None, pass_manager=None,
qobj_id=None, qobj_header=None, shots=1024, # common run options
memory=False, max_credits=10, seed_simulator=None,
default_qubit_los=None, default_meas_los=None, # schedule run options
schedule_los=None, meas_level=2, meas_return='avg',
memory_slots=None, memory_slot_size=100, rep_time=None, parameter_binds=None,
**run_config):
# transpiling the circuits using given transpile options
experiments = transpile(experiments,
basis_gates=basis_gates,
coupling_map=coupling_map,
backend_properties=backend_properties,
initial_layout=initial_layout,
seed_transpiler=seed_transpiler,
optimization_level=optimization_level,
backend=backend,
pass_manager=pass_manager,
)
# assembling the circuits into a qobj to be run on the backend
qobj = assemble(experiments,
qobj_id=qobj_id,
qobj_header=qobj_header,
shots=shots,
memory=memory,
max_credits=max_credits,
seed_simulator=seed_simulator,
default_qubit_los=default_qubit_los,
default_meas_los=default_meas_los,
schedule_los=schedule_los,
meas_level=meas_level,
meas_return=meas_return,
memory_slots=memory_slots,
memory_slot_size=memory_slot_size,
rep_time=rep_time,
parameter_binds=parameter_binds,
backend=backend,
**run_config
)
# executing the circuits on the backend and returning the job
return backend.run(qobj, **run_config)
Dieses Mal werde ich nicht auf die Details von Transpile eingehen. Ich schaue wirklich nur auf die Oberfläche.
Sie finden es unter qiskit / compiler / transpile.py. Ich werde es weglassen, weil der Docstring sehr lang ist, aber es ist interessant und ich empfehle, es zu lesen.
def transpile(circuits,
backend=None,
basis_gates=None, coupling_map=None, backend_properties=None,
initial_layout=None, seed_transpiler=None,
optimization_level=None,
pass_manager=None, callback=None, output_name=None):
# transpiling schedules is not supported yet.
if isinstance(circuits, Schedule) or \
(isinstance(circuits, list) and all(isinstance(c, Schedule) for c in circuits)):
return circuits
if optimization_level is None:
config = user_config.get_config()
optimization_level = config.get('transpile_optimization_level', None)
# Get TranspileConfig(s) to configure the circuit transpilation job(s)
circuits = circuits if isinstance(circuits, list) else [circuits]
transpile_configs = _parse_transpile_args(circuits, backend, basis_gates, coupling_map,
backend_properties, initial_layout,
seed_transpiler, optimization_level,
pass_manager, callback, output_name)
# Check circuit width against number of qubits in coupling_map(s)
coupling_maps_list = list(config.coupling_map for config in transpile_configs)
for circuit, parsed_coupling_map in zip(circuits, coupling_maps_list):
# If coupling_map is not None
if isinstance(parsed_coupling_map, CouplingMap):
n_qubits = len(circuit.qubits)
max_qubits = parsed_coupling_map.size()
if n_qubits > max_qubits:
raise TranspilerError('Number of qubits ({}) '.format(n_qubits) +
'in {} '.format(circuit.name) +
'is greater than maximum ({}) '.format(max_qubits) +
'in the coupling_map')
# Transpile circuits in parallel
circuits = parallel_map(_transpile_circuit, list(zip(circuits, transpile_configs)))
if len(circuits) == 1:
return circuits[0]
return circuits
In tatsächlichen Maschinen gibt es Gates, die nicht mit CNOT verbunden sind. Es scheint also, dass sie daran arbeiten, Schaltkreise zu erstellen, während sie diese zuweisen. Da die Berechnung selbst schwierig ist, wird sie außerdem parallel berechnet. Selbst wenn Sie das Argument weglassen, werden die Einstellungen entsprechend vom Back-End abgerufen, da dem Back-End viele Einstellungen hinzugefügt wurden.
Konvertiert die Schaltung und gibt die Schaltung selbst zurück.
Sehen Sie sich das assemble
in qiskit / compiler / assemble.py auf die gleiche Weise an. (Docstring weggelassen)
def assemble(experiments,
backend=None,
qobj_id=None, qobj_header=None,
shots=1024, memory=False, max_credits=None, seed_simulator=None,
qubit_lo_freq=None, meas_lo_freq=None,
qubit_lo_range=None, meas_lo_range=None,
schedule_los=None, meas_level=2, meas_return='avg', meas_map=None,
memory_slot_size=100, rep_time=None, parameter_binds=None,
**run_config):
experiments = experiments if isinstance(experiments, list) else [experiments]
qobj_id, qobj_header, run_config_common_dict = _parse_common_args(backend, qobj_id, qobj_header,
shots, memory, max_credits,
seed_simulator, **run_config)
# assemble either circuits or schedules
if all(isinstance(exp, QuantumCircuit) for exp in experiments):
run_config = _parse_circuit_args(parameter_binds, **run_config_common_dict)
# If circuits are parameterized, bind parameters and remove from run_config
bound_experiments, run_config = _expand_parameters(circuits=experiments,
run_config=run_config)
return assemble_circuits(circuits=bound_experiments, qobj_id=qobj_id,
qobj_header=qobj_header, run_config=run_config)
elif all(isinstance(exp, ScheduleComponent) for exp in experiments):
run_config = _parse_pulse_args(backend, qubit_lo_freq, meas_lo_freq,
qubit_lo_range, meas_lo_range,
schedule_los, meas_level, meas_return,
meas_map, memory_slot_size, rep_time,
**run_config_common_dict)
return assemble_schedules(schedules=experiments, qobj_id=qobj_id,
qobj_header=qobj_header, run_config=run_config)
else:
raise QiskitError("bad input to assemble() function; "
"must be either circuits or schedules")
Es scheint, dass Sie die Schaltung und den Impulsplan zusammenstellen können. Der Typ des zurückzugebenden Werts ist "QObj", und im Fall einer Schaltung wird der Typ "QasmQObj" zurückgegeben.
Wenn Sie nicht wissen, welche Art von Datenstruktur der Typ "QasmQobj" ist, werden Sie danach in Schwierigkeiten geraten
from qiskit import *
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0,1], [0,1])
backend_sim = BasicAer.get_backend('qasm_simulator')
tran = transpile(qc, backend=backend_sim)
asm = assemble(qc, backend=backend_sim)
Mit Blick auf das "Asm" von war es wie folgt.
QasmQobj(
config=QasmQobjConfig(
memory=False,
memory_slots=2,
n_qubits=2,
parameter_binds=[],
shots=1024),
experiments=[
QasmQobjExperiment(
config=QasmQobjExperimentConfig(memory_slots=2, n_qubits=2),
header=QobjExperimentHeader(
clbit_labels=[['c', 0], ['c', 1]],
creg_sizes=[['c', 2]],
memory_slots=2,
n_qubits=2,
name='circuit0',
qreg_sizes=[['q', 2]],
qubit_labels=[['q', 0], ['q', 1]]),
instructions=[
QasmQobjInstruction(
name='u2', params=[0, 3.14159265358979], qubits=[0]),
QasmQobjInstruction(name='cx', qubits=[0, 1]),
QasmQobjInstruction(memory=[0], name='measure', qubits=[0]),
QasmQobjInstruction(memory=[1], name='measure', qubits=[1])
])
],
header=QobjHeader(backend_name='qasm_simulator', backend_version='2.0.0'),
qobj_id='06682d2e-bfd8-4dba-ba8e-4e46492c1609',
schema_version='1.1.0',
type='QASM')
Ich denke, Sie können es ohne Probleme lesen. Das H-Gate wird übrigens in "u2 (0, π)" umgewandelt. Dies ist ein äquivalenter Ausdruck.
backend.run
Nachdem wir die Schaltung als nächstes auf "QasmQobj" umgestellt haben
return backend.run(qobj, **run_config)
Ich werde lesen. qiskit / provider / basicaer / qasm_simulator.py
def run(self, qobj, backend_options=None):
"""Run qobj asynchronously.
Args:
qobj (Qobj): payload of the experiment
backend_options (dict): backend options
Returns:
BasicAerJob: derived from BaseJob
Additional Information:
backend_options: Is a dict of options for the backend. It may contain
* "initial_statevector": vector_like
The "initial_statevector" option specifies a custom initial
initial statevector for the simulator to be used instead of the all
zero state. This size of this vector must be correct for the number
of qubits in all experiments in the qobj.
Example::
backend_options = {
"initial_statevector": np.array([1, 0, 0, 1j]) / np.sqrt(2),
}
"""
self._set_options(qobj_config=qobj.config,
backend_options=backend_options)
job_id = str(uuid.uuid4())
job = BasicAerJob(self, job_id, self._run_job, qobj)
job.submit()
return job
Es ist schön, einen Anfangsvektor in die Optionen einfügen zu können. Es sieht so aus, als ob es vollständig vom Backend abhängig ist. Die Option "chop_threshold" scheint den Wert als "0" zu behandeln, wenn er bei der Simulation des Zustandsvektors kleiner oder gleich diesem Wert ist.
QasmSimulatorPy._set_options
Lesen Sie weiter _set_options
.
def _set_options(self, qobj_config=None, backend_options=None):
"""Set the backend options for all experiments in a qobj"""
# Reset default options
self._initial_statevector = self.DEFAULT_OPTIONS["initial_statevector"]
self._chop_threshold = self.DEFAULT_OPTIONS["chop_threshold"]
if backend_options is None:
backend_options = {}
# Check for custom initial statevector in backend_options first,
# then config second
if 'initial_statevector' in backend_options:
self._initial_statevector = np.array(backend_options['initial_statevector'],
dtype=complex)
elif hasattr(qobj_config, 'initial_statevector'):
self._initial_statevector = np.array(qobj_config.initial_statevector,
dtype=complex)
if self._initial_statevector is not None:
# Check the initial statevector is normalized
norm = np.linalg.norm(self._initial_statevector)
if round(norm, 12) != 1:
raise BasicAerError('initial statevector is not normalized: ' +
'norm {} != 1'.format(norm))
# Check for custom chop threshold
# Replace with custom options
if 'chop_threshold' in backend_options:
self._chop_threshold = backend_options['chop_threshold']
elif hasattr(qobj_config, 'chop_threshold'):
self._chop_threshold = qobj_config.chop_threshold
Aufgrund der Tatsache, dass das Backend selbst Optionen hat
# Reset default options
self._initial_statevector = self.DEFAULT_OPTIONS["initial_statevector"]
self._chop_threshold = self.DEFAULT_OPTIONS["chop_threshold"]
Ist an einem solchen Ort herausgekommen. Wie auch immer, ich interpretiere die beiden Optionen einfach und setze sie in qobj.
BasicAerJob .__ init__
job_id = str(uuid.uuid4())
job = BasicAerJob(self, job_id, self._run_job, qobj)
Es hat eine geeignete UUID als job_id.
Ich mache BasicAerJob
als Job. self._run_job
ist eine Methode, aber wir übergeben die Methode so wie sie ist. Mal sehen, wenn wir später angerufen werden.
qiskit/providers/basicaer/basicaerjob.py
class BasicAerJob(BaseJob):
"""BasicAerJob class.
Attributes:
_executor (futures.Executor): executor to handle asynchronous jobs
"""
if sys.platform in ['darwin', 'win32']:
_executor = futures.ThreadPoolExecutor()
else:
_executor = futures.ProcessPoolExecutor()
def __init__(self, backend, job_id, fn, qobj):
super().__init__(backend, job_id)
self._fn = fn
self._qobj = qobj
self._future = None
Ich habe gerade die Daten erstellt und noch nichts getan. Ich war ein wenig besorgt über die Verwendung des Thread-Pools unter Windows und Mac und des Prozess-Pools unter anderen. Beim Multithreading kann auch bei mehreren CPUs nur ein Thread gleichzeitig ausgeführt werden. Seien wir vorsichtig, wie es verwendet wird.
BasicAerJob.submit
job.submit()
return job
Ich reiche einen Job ein und gebe dann einen Job zurück. Lesen wir "BasicAerJob.submit".
def submit(self):
"""Submit the job to the backend for execution.
Raises:
QobjValidationError: if the JSON serialization of the Qobj passed
during construction does not validate against the Qobj schema.
JobError: if trying to re-submit the job.
"""
if self._future is not None:
raise JobError("We have already submitted the job!")
validate_qobj_against_schema(self._qobj)
self._future = self._executor.submit(self._fn, self._job_id, self._qobj)
validate_qobj_against_schema(self._qobj)
Entspricht Qobj dem Format des in einer anderen Datei definierten JSON-Schemas? Wurde nur mit der externen Bibliothek jsonschema
verifiziert. Es ist nicht sehr interessant, also werde ich es weglassen.
_executor
war der ThreadPoolExecutor
oder ProcessPoolExecutor
in der Python-Standardbibliothekconcurrent.futures
. Wir nennen diese "Submit" -Methode.
Lesen Sie die offiziellen Dokumente.
submit(fn, *args, **kwargs)
Planen Sie die Ausführung des aufrufbaren Objekts "fn" als "fn (* args ** kwargs)" und stellen Sie die Ausführung des aufrufbaren Objekts [Future] dar (https://docs.python.org/en). /3/library/concurrent.futures.html#concurrent.futures.Future) Gibt ein Objekt zurück.
Mit anderen Worten, Sie erstellen ein "Future" -Objekt, um "self._fn (self._job_id, self._qobj)" für die Ausführung zu planen.
Dieses "Future" -Objekt dient zur asynchronen Ausführung. Während es von "_executor" hinter die Kulissen verschoben wird, wird der Vorgang auch dann fortgesetzt, wenn die Berechnung nicht abgeschlossen ist. (Wenn Sie einen Hamburger in einem Hamburgerladen bestellen, können Sie vor dem Schalter warten, bis er fertig ist. Wenn Sie jedoch ein Nummernschild erhalten, können Sie sich zuerst setzen oder etwas anderes tun und Ihre Zeit sinnvoll verbringen. Bei regulären Funktionsaufrufen müssen Sie warten, bis das Ergebnis zurückkommt. Bei asynchroner Ausführung können Sie jedoch etwas anderes tun, ohne darauf zu warten, dass es zurückkommt. Das Objekt "Future" erhält in Zukunft den Hamburger. Es ist wie ein Nummernschild
QasmSimulatorPy._run_job
Erinnern an das, was "self._fn (self._job_id, self._qobj)" war, angetrieben von "_executor", "self made with" job = BasicAerJob (self, job_id, self._run_job, qobj) " Es war ._run_job (job_id, qobj) `.
Lesen wir also "QasmSimulatorPy._run_job".
def _run_job(self, job_id, qobj):
"""Run experiments in qobj
Args:
job_id (str): unique id for the job.
qobj (Qobj): job description
Returns:
Result: Result object
"""
self._validate(qobj)
result_list = []
self._shots = qobj.config.shots
self._memory = getattr(qobj.config, 'memory', False)
self._qobj_config = qobj.config
start = time.time()
for experiment in qobj.experiments:
result_list.append(self.run_experiment(experiment))
end = time.time()
result = {'backend_name': self.name(),
'backend_version': self._configuration.backend_version,
'qobj_id': qobj.qobj_id,
'job_id': job_id,
'results': result_list,
'status': 'COMPLETED',
'success': True,
'time_taken': (end - start),
'header': qobj.header.to_dict()}
return Result.from_dict(result)
--self._validate
(später lesen)
--Stellen Sie die erforderlichen Informationen im Backend selbst ein
QasmSimulatorPy._validate
def _validate(self, qobj):
"""Semantic validations of the qobj which cannot be done via schemas."""
n_qubits = qobj.config.n_qubits
max_qubits = self.configuration().n_qubits
if n_qubits > max_qubits:
raise BasicAerError('Number of qubits {} '.format(n_qubits) +
'is greater than maximum ({}) '.format(max_qubits) +
'for "{}".'.format(self.name()))
for experiment in qobj.experiments:
name = experiment.header.name
if experiment.config.memory_slots == 0:
logger.warning('No classical registers in circuit "%s", '
'counts will be empty.', name)
elif 'measure' not in [op.name for op in experiment.instructions]:
logger.warning('No measurements in circuit "%s", '
'classical register will remain all zeros.', name)
Wir prüfen, ob die Anzahl der Qubits den Maximalwert nicht überschreitet und ob es ein klassisches Register gibt und ob eine Messung durchgeführt wird. (Kein klassisches Register, keine Messung wird als Warnung behandelt, kein Fehler)
QasmSimulatorPy.run_experiment
Erinnern wir uns vorher daran, woraus die "Experimente" gemacht wurden.
experiments=[
QasmQobjExperiment(
config=QasmQobjExperimentConfig(memory_slots=2, n_qubits=2),
header=QobjExperimentHeader(
clbit_labels=[['c', 0], ['c', 1]],
creg_sizes=[['c', 2]],
memory_slots=2,
n_qubits=2,
name='circuit0',
qreg_sizes=[['q', 2]],
qubit_labels=[['q', 0], ['q', 1]]),
instructions=[
QasmQobjInstruction(
name='u2', params=[0, 3.14159265358979], qubits=[0]),
QasmQobjInstruction(name='cx', qubits=[0, 1]),
QasmQobjInstruction(memory=[0], name='measure', qubits=[0]),
QasmQobjInstruction(memory=[1], name='measure', qubits=[1])
])
],
"QasmQobjExperiment" entspricht einer Quantenschaltung. Mit Qiskit können Sie eine Reihe von Quantenschaltungen packen und ausführen, aber diesmal scheint es nur eine zu geben.
Da wir die Schleife von for experiment in qobj.experiments:
ausgeführt haben, rufen wir für jede Schaltung run_experiment
auf. Die Schaltung hat "Anweisungen", die Gates und Messungen entsprechen.
run_experiment
ist wirklich lang, also werde ich es weglassen. Grob gesagt wird das Tor berechnet und die Messung wird für die Anzahl der "Schüsse" berechnet.
return {'name': experiment.header.name,
'seed_simulator': seed_simulator,
'shots': self._shots,
'data': data,
'status': 'DONE',
'success': True,
'time_taken': (end - start),
'header': experiment.header.to_dict()}
Gibt ein Ergebnis wie zurück. Außerdem ist "Daten"
# Add data
data = {'counts': dict(Counter(memory))}
# Optionally add memory list
if self._memory:
data['memory'] = memory
# Optionally add final statevector
if self.SHOW_FINAL_STATE:
data['statevector'] = self._get_statevector()
# Remove empty counts and memory for statevector simulator
if not data['counts']:
data.pop('counts')
if 'memory' in data and not data['memory']
Wie es hat Messdaten.
qiskit.result.Result
Der Wörterbuchtyp wird durch "return Result.from_dict (result)" in "Result" geändert. qiskit/result/result.py
@bind_schema(ResultSchema)
class Result(BaseModel):
"""Model for Results.
Please note that this class only describes the required fields. For the
full description of the model, please check ``ResultSchema``.
Attributes:
backend_name (str): backend name.
backend_version (str): backend version, in the form X.Y.Z.
qobj_id (str): user-generated Qobj id.
job_id (str): unique execution id from the backend.
success (bool): True if complete input qobj executed correctly. (Implies
each experiment success)
results (list[ExperimentResult]): corresponding results for array of
experiments of the input qobj
"""
def __init__(self, backend_name, backend_version, qobj_id, job_id, success,
results, **kwargs):
self.backend_name = backend_name
self.backend_version = backend_version
self.qobj_id = qobj_id
self.job_id = job_id
self.success = success
self.results = results
Ursprünglich möchte ich "from_dict" lesen, aber eines der Merkmale von qiskit-terra ist, dass ich mein Bestes tue, um das JSON-Schema zu verarbeiten. from_dict
ist in der übergeordneten Klasse BaseModel
definiert und deren Inhalt sind
@classmethod
def from_dict(cls, dict_):
"""Deserialize a dict of simple types into an instance of this class.
Note that this method requires that the model is bound with
``@bind_schema``.
"""
try:
data = cls.schema.load(dict_)
except ValidationError as ex:
raise ModelValidationError(
ex.messages, ex.field_name, ex.data, ex.valid_data, **ex.kwargs) from None
return data
Es ist sehr einfach. Erstellen Sie eine "ResultSchema" -Klasse getrennt von der "Result" -Klasse
class ResultSchema(BaseSchema):
"""Schema for Result."""
# Required fields.
backend_name = String(required=True)
backend_version = String(required=True,
validate=Regexp('[0-9]+.[0-9]+.[0-9]+$'))
qobj_id = String(required=True)
job_id = String(required=True)
success = Boolean(required=True)
results = Nested(ExperimentResultSchema, required=True, many=True)
# Optional fields.
date = DateTime()
status = String()
header = Nested(ObjSchema)
Und so weiter geht es mir ziemlich gut. (Definiert unter /qiskit/result/models.py) Ich kann es nicht richtig lesen, aber ich denke, dass "ExperimentResultSchema" usw. rekursiv in Objekte umgewandelt werden, während bestätigt wird, dass sie diese Datentypen haben.
In jedem Fall handelt es sich um eine Datenauffüllung. Lassen Sie mich diesmal darauf verzichten.
Das "Ergebnis", das ich bewegte und zurückgab, war wie folgt.
Result(
backend_name='qasm_simulator',
backend_version='2.0.0',
header=Obj(backend_name='qasm_simulator', backend_version='2.0.0'),
job_id='65b162ae-fe6b-480a-85ba-8c890d8bbf3b',
qobj_id='c75e9b2c-da7c-4dd6-96da-d132126e6dc0',
results=[
ExperimentResult(
data=ExperimentResultData(counts=Obj(0x0=493, 0x3=531)),
header=Obj(
clbit_labels=[['c', 0], ['c', 1]],
creg_sizes=[['c', 2]],
memory_slots=2,
n_qubits=2,
name='circuit0',
qreg_sizes=[['q', 2]],
qubit_labels=[['q', 0], ['q', 1]]),
meas_level=2,
name='circuit0',
seed_simulator=1480888590,
shots=1024,
status='DONE',
success=True,
time_taken=0.12355875968933105)
],
status='COMPLETED',
success=True,
time_taken=0.12369537353515625)
Ich habe die Mitte übersprungen, aber
result = execute(qc, backend_sim).result()
Die Ausführung (qc, backend_sim) von ist beendet, und wir werden uns die .result () ansehen.
execute
hat BasicAerJob
zurückgegeben.
@requires_submit
def result(self, timeout=None):
# pylint: disable=arguments-differ
"""Get job result. The behavior is the same as the underlying
concurrent Future objects,
https://docs.python.org/3/library/concurrent.futures.html#future-objects
Args:
timeout (float): number of seconds to wait for results.
Returns:
qiskit.Result: Result object
Raises:
concurrent.futures.TimeoutError: if timeout occurred.
concurrent.futures.CancelledError: if job cancelled before completed.
"""
return self._future.result(timeout=timeout)
Das erste "@ require_submit" sieht aus, wenn Sie ein "submit" durchgeführt haben. Unabhängig davon, ob es "übermittelt" wurde oder nicht, wurde beim Erstellen des "BasicAerJob" auf "self._future = None" gesetzt. Bei "Submission" ist das "Future" -Objekt in "self._future" enthalten, sodass es auf "None" gesetzt ist. Sie können dies herausfinden, indem Sie überprüfen, ob dies nicht der Fall ist.
Dies ruft normalerweise die "result" -Methode des "Future" -Objekts auf. Siehe die offizielle Dokumentation (https://docs.python.org/ja/3/library/concurrent.futures.html#concurrent.futures.Future).
result(timeout=None)
Gibt den vom Aufruf zurückgegebenen Wert zurück. Wenn der Anruf noch nicht abgeschlossen ist, wartet diese Methode auf Timeout-Sekunden. Wenn der Aufruf nicht innerhalb der Timeout-Sekunden abgeschlossen wird, wird ein concurrent.futures.TimeoutError ausgelöst. Sie können int oder float für das Timeout angeben. Wenn kein Timeout angegeben ist oder None ist, ist die Wartezeit unbegrenzt. CanceledError wird ausgelöst, wenn es abgebrochen wird, bevor die Zukunft abgeschlossen ist. Wenn der Aufruf eine Ausnahme auslöst, löst diese Methode dieselbe Ausnahme aus.
Es ist eine Methode, die das Ergebnis zurückgibt, wenn es fertig ist, wartet, wenn es nicht fertig ist, und dann das Ergebnis zurückgibt. Das Ergebnis ist ein Objekt vom Typ "Ergebnis", wie oben gezeigt.
Result.get_counts
Endlich die letzte Zeile.
print(result.get_counts(qc))
Ich werde schauen.
def get_counts(self, experiment=None):
"""Get the histogram data of an experiment.
Args:
experiment (str or QuantumCircuit or Schedule or int or None): the index of the
experiment, as specified by ``get_data()``.
Returns:
dict[str:int]: a dictionary with the counts for each qubit, with
the keys containing a string in binary format and separated
according to the registers in circuit (e.g. ``0100 1110``).
The string is little-endian (cr[0] on the right hand side).
Raises:
QiskitError: if there are no counts for the experiment.
"""
exp = self._get_experiment(experiment)
try:
header = exp.header.to_dict()
except (AttributeError, QiskitError): # header is not available
header = None
if 'counts' in self.data(experiment).keys():
return postprocess.format_counts(self.data(experiment)['counts'],
header)
elif 'statevector' in self.data(experiment).keys():
vec = postprocess.format_statevector(self.data(experiment)['statevector'])
return state_to_counts(vec)
else:
raise QiskitError('No counts for experiment "{0}"'.format(experiment))
In "_get_experiment" wird die "Erfahrung" entsprechend dem Schaltungsnamen extrahiert. Wenn es diesmal nur ein "Experiment" gibt, können Sie das Argument "Erfahrung" weglassen.
Wenn Sie den Header aus "Experiment" extrahieren können, kennen Sie die Länge des klassischen Registers, sodass Sie das Messergebnis gut auf Null setzen können.
In format_counts
mache ich eine nette Konvertierung in eine Zeichenkette und packe sie in ein Wörterbuch.
Dieses Mal habe ich mit Qiskit den Verarbeitungsfluss gelesen, mit Ausnahme des Teils, der direkt mit der Quantenberechnung zusammenhängt. Aufgrund wiederholter Änderungen und zukunftssicherer Erweiterbarkeit gab es einige Teile, die sehr schwierige Spezifikationen hatten, aber es wurde gemacht, um eine Menge Dinge tun zu können, und es wurde auch in Transpile und JSON usw. konvertiert. Ich hatte den Eindruck, dass die Teile, mit denen sich Blueqat nicht befasst, ziemlich schwer sind und es eine großartige Lernerfahrung sein wird.
Dies ist das Ende meines Ziels, den gesamten Prozess zu lesen, aber ich werde weiterhin den Qiskit-Quellcode lesen.