Entwickelt sich. https://github.com/Blueqat/Blueqat
Erstellen eines Blueqat-Backends ~ Teil 1 erklärte, wie ein Simulator-Backend erstellt wird, das OpenQ ASM-Eingaben akzeptieren kann. Dieses Mal werde ich erklären, wie ein Backend im allgemeinen Fall erstellt wird, in dem dies nicht der Fall ist.
Sie können mindestens ein Backend erstellen, indem Sie "Backend" erben und die "run" -Methode implementieren.
Es implementiert auch die copy
-Methode neu, wenn eine Kopie des Backend-Objekts selbst für copy.deepcopy
nicht geeignet ist.
Die run
Methode hat die Form run (self, Gates, n_qubits, * args, ** kwargs)
.
Gates
ist die Liste der Quanten-Gate-Objekte, n_qubits
ist die Anzahl der Quantenbits, * args
, ** kwargs
ist, wenn der Benutzer Circuit (). Run ()
aufruft Das Argument von "run" wird übergeben.
Wenn Sie dies implementieren, können Sie ein Backend erstellen. Da es jedoch zu viel für den Entwickler bedeutet, haben wir den folgenden Ablauf vorbereitet. Es klingt verwirrend, aber wenn Sie dem Ablauf folgen, kann die Implementierung etwas einfacher sein. Wenn Sie die "run" -Methode erneut implementieren, können Sie auch die von Ihnen vorbereitete verwenden.
Standardmäßig ruft der Aufruf der Methode "run" den folgenden Code auf.
def _run(self, gates, n_qubits, args, kwargs):
gates, ctx = self._preprocess_run(gates, n_qubits, args, kwargs)
self._run_gates(gates, n_qubits, ctx)
return self._postprocess_run(ctx)
def _run_gates(self, gates, n_qubits, ctx):
"""Iterate gates and call backend's action for each gates"""
for gate in gates:
action = self._get_action(gate)
if action is not None:
ctx = action(gate, ctx)
else:
ctx = self._run_gates(gate.fallback(n_qubits), n_qubits, ctx)
return ctx
Was Backend-Entwickler implementieren sollten
--_preprocess_run
Methode
_postprocess_run
Methodeist.
Beachten Sie die Variable mit der Bezeichnung "ctx" hier. Diese Variable ist eine Variable, die den Status vom Anfang bis zum Ende von "run" hält. (Wenn Sie es nicht brauchen, können Sie "None" verwenden, aber ich denke, es gibt wenige Fälle, in denen Sie es überhaupt nicht brauchen.) Da das Backend selbst auch ein gewöhnliches Objekt ist, können Sie ihm einen Status geben, indem Sie "self.foo = ..." usw. setzen. Dies führt jedoch zu Fehlern usw. Verwenden Sie daher so oft wie möglich "ctx".
Als Beispiel dafür, was in ctx
gespeichert werden soll
Und so weiter. (Achten Sie darauf, dass Sie es während der Ausführung nicht wissen, es sei denn, Sie belassen die Anzahl der Quantenbits in "ctx".)
Wenn Sie sich den obigen Code ansehen, achten Sie auch auf "ctx"
--Erstelle ein ctx
Objekt mit _preprocess_run
_postprocess_run
empfängt ctx
und gibt das Ergebnis von run
zurückEs ist ein Fluss geworden.
Quantenschaltungen werden durch Anordnen von Gates hergestellt. Eine typische Backend-Implementierungsmethode besteht darin, die Gates nacheinander in einer Reihe anzuwenden.
Die Operation des Anlegens eines Gates wird hier als Aktion bezeichnet. Um eine Aktion zu implementieren, fügen Sie einfach eine Methode hinzu:
def gate_{Torname}(self, gate, ctx):
#Etwas umgesetzt
return ctx
Machen.
Als Merkmal von Blueqat gab es eine Slice-Notation wie "Circuit (). H [:]", aber es gibt auch eine Methode zum Trennen der Slice-Notation "für idx in gate.target_iter (Anzahl der Bits) Wenn Sie möchten, können Sie den Index von 1 Quantenbitgatter erhalten. Außerdem sollte der Index von 2 Quantenbitgattern wie CNOT "für c, t in gate.control_target_iter (Anzahl der Quantenbits)" sein. Die Anzahl der Quantenbits wird hier benötigt, um die Anzahl der Quantenbits zu kennen, die bei Verwendung von "Circuit (). H [:]" angewendet werden sollen.
Nicht alle Tore müssen implementiert werden. Zum Beispiel können T-Gatter und S-Gatter hergestellt werden, wenn es ein rotierendes Z-Gatter (RZ) gibt. Wenn es auf der Blueqat-Seite nicht implementiert ist, achten Sie darauf, stattdessen ein anderes Gatter zu verwenden. Ich bin.
Wenn es kein alternatives Gate gibt, ist es ein Fehler, wenn es verwendet wird, aber es ist kein Fehler, wenn es nicht verwendet wird, sodass Sie ein Backend erstellen können, das nur einige Gates unterstützt. (Wenn Sie beispielsweise nur X-Gatter, CX-Gatter und CCX-Gatter implementieren, können Sie ein Backend erstellen, das auf klassische Logikschaltungen spezialisiert ist. Sie können es mit nur einer Bitoperation implementieren, sodass Sie etwas erstellen können, das mit hoher Geschwindigkeit arbeitet.)
Gates, die implementiert werden müssen, können in Zukunft, aber derzeit, aussortiert werden Die Messungen sind X, Y, Z, H, CZ, CX, RX, RY, RZ, CCZ, U3. (Maßnahmen definieren Aktionen genauso wie Tore.)
Ein etwas ungewöhnliches Backend ist QasmOutputBackend
.
Dies ist kein Simulator, sondern ein Backend für die Konvertierung der Blueqat-Quantenschaltungen in OpenQ ASM.
ctx
enthält eine Liste von OpenQASM-Zeilen und die Anzahl von Quantenbits.
Außerdem fügt jede Aktion der Liste eine Zeile hinzu.
Klicken Sie hier für den gesamten Code.
class QasmOutputBackend(Backend):
"""Backend for OpenQASM output."""
def _preprocess_run(self, gates, n_qubits, args, kwargs):
def _parse_run_args(output_prologue=True, **_kwargs):
return { 'output_prologue': output_prologue }
args = _parse_run_args(*args, **kwargs)
if args['output_prologue']:
qasmlist = [
"OPENQASM 2.0;",
'include "qelib1.inc";',
f"qreg q[{n_qubits}];",
f"creg c[{n_qubits}];",
]
else:
qasmlist = []
return gates, (qasmlist, n_qubits)
def _postprocess_run(self, ctx):
return "\n".join(ctx[0])
def _one_qubit_gate_noargs(self, gate, ctx):
for idx in gate.target_iter(ctx[1]):
ctx[0].append(f"{gate.lowername} q[{idx}];")
return ctx
gate_x = _one_qubit_gate_noargs
gate_y = _one_qubit_gate_noargs
gate_z = _one_qubit_gate_noargs
gate_h = _one_qubit_gate_noargs
gate_t = _one_qubit_gate_noargs
gate_s = _one_qubit_gate_noargs
def _two_qubit_gate_noargs(self, gate, ctx):
for control, target in gate.control_target_iter(ctx[1]):
ctx[0].append(f"{gate.lowername} q[{control}],q[{target}];")
return ctx
gate_cz = _two_qubit_gate_noargs
gate_cx = _two_qubit_gate_noargs
gate_cy = _two_qubit_gate_noargs
gate_ch = _two_qubit_gate_noargs
gate_swap = _two_qubit_gate_noargs
def _one_qubit_gate_args_theta(self, gate, ctx):
for idx in gate.target_iter(ctx[1]):
ctx[0].append(f"{gate.lowername}({gate.theta}) q[{idx}];")
return ctx
gate_rx = _one_qubit_gate_args_theta
gate_ry = _one_qubit_gate_args_theta
gate_rz = _one_qubit_gate_args_theta
def gate_i(self, gate, ctx):
for idx in gate.target_iter(ctx[1]):
ctx[0].append(f"id q[{idx}];")
return ctx
def gate_u1(self, gate, ctx):
for idx in gate.target_iter(ctx[1]):
ctx[0].append(f"{gate.lowername}({gate.lambd}) q[{idx}];")
return ctx
def gate_u2(self, gate, ctx):
for idx in gate.target_iter(ctx[1]):
ctx[0].append(f"{gate.lowername}({gate.phi},{gate.lambd}) q[{idx}];")
return ctx
def gate_u3(self, gate, ctx):
for idx in gate.target_iter(ctx[1]):
ctx[0].append(f"{gate.lowername}({gate.theta},{gate.phi},{gate.lambd}) q[{idx}];")
return ctx
def gate_cu1(self, gate, ctx):
for c, t in gate.control_target_iter(ctx[1]):
ctx[0].append(f"{gate.lowername}({gate.lambd}) q[{c}],q[{t}];")
return ctx
def gate_cu2(self, gate, ctx):
for c, t in gate.control_target_iter(ctx[1]):
ctx[0].append(f"{gate.lowername}({gate.phi},{gate.lambd}) q[{c}],q[{t}];")
return ctx
def gate_cu3(self, gate, ctx):
for c, t in gate.control_target_iter(ctx[1]):
ctx[0].append(f"{gate.lowername}({gate.theta},{gate.phi},{gate.lambd}) q[{c}],q[{t}];")
return ctx
def _three_qubit_gate_noargs(self, gate, ctx):
c0, c1, t = gate.targets
ctx[0].append(f"{gate.lowername} q[{c0}],q[{c1}],q[{t}];")
return ctx
gate_ccx = _three_qubit_gate_noargs
gate_cswap = _three_qubit_gate_noargs
def gate_measure(self, gate, ctx):
for idx in gate.target_iter(ctx[1]):
ctx[0].append(f"measure q[{idx}] -> c[{idx}];")
return ctx
_preprocess_run
def _preprocess_run(self, gates, n_qubits, args, kwargs):
def _parse_run_args(output_prologue=True, **_kwargs):
return { 'output_prologue': output_prologue }
args = _parse_run_args(*args, **kwargs)
if args['output_prologue']:
qasmlist = [
"OPENQASM 2.0;",
'include "qelib1.inc";',
f"qreg q[{n_qubits}];",
f"creg c[{n_qubits}];",
]
else:
qasmlist = []
return gates, (qasmlist, n_qubits)
Das erste, was wir tun, ist die Analyse von Optionen.
Da es mit der Option output_prologue
wie Circuit () ausgeführt werden kann. Run (backend = 'qasm_output', output_prologue = False)
wird die Option analysiert.
Diese Option ist standardmäßig True, wird jedoch am Anfang von OpenQ ASM hinzugefügt, wenn False angegeben wird.
OPENQASM 2.0;
include "qelib1.inc";
qreg q[Anzahl der Bits];
creg c[Anzahl der Bits];
Wurde weggelassen.
Als nächstes ist ctx eine Liste von OpenQASM-Zeilen und ein Tupel von Quantenbits.
Es gibt Gates und ctx zurück, aber Gates gibt nur das zurück, was als Argument übergeben wurde.
Ich habe mich entschieden, Gates mit _preprocess_run
zurückzugeben, weil ich die Gate-Spalte für die Optimierung usw. manipulieren wollte. Wenn dies jedoch nicht besonders erforderlich ist, wird das empfangene Argument so zurückgegeben, wie es ist.
def _one_qubit_gate_noargs(self, gate, ctx):
for idx in gate.target_iter(ctx[1]):
ctx[0].append(f"{gate.lowername} q[{idx}];")
return ctx
gate_x = _one_qubit_gate_noargs
gate_y = _one_qubit_gate_noargs
gate_z = _one_qubit_gate_noargs
#Ausgelassen, weil es viele gibt
def gate_cu3(self, gate, ctx):
for c, t in gate.control_target_iter(ctx[1]):
ctx[0].append(f"{gate.lowername}({gate.theta},{gate.phi},{gate.lambd}) q[{c}],q[{t}];")
return ctx
Ich werde es so machen. Da es schwierig ist, x-, y- und z-Tore nacheinander herzustellen, trage ich sie seitwärts. Wenn Sie es nicht seitlich tragen können, implementieren Sie es vorsichtig wie "gate_cu3". Was wir tun ist, dass "ctx" war "([Liste der Zeilen], Anzahl der Quantenbits)", also fügen Sie der Liste der Zeilen mit "ctx [0] .append (...)" eine neue Zeile hinzu Ich mache es einfach.
_postprocess_run
def _postprocess_run(self, ctx):
return "\n".join(ctx[0])
Es wird einfach eine Liste von Zeilen als durch Zeilenumbrüche getrennte Zeichenfolge zurückgegeben. Dieses Ergebnis ist das Ergebnis von "run".
Beim letzten Mal haben wir uns die Backend-Implementierungsmethode für Verarbeitungssysteme angesehen, die OpenQ ASM lesen können. Diesmal haben wir uns jedoch die allgemeinere Backend-Implementierungsmethode angesehen.
Bei Blueqat möchten wir uns auf der Bibliotheksseite so weit wie möglich darum kümmern und Entwicklern und Benutzern ermöglichen, das zu tun, was sie wollen. Außerdem können Sie auf einfache Weise ein Backend erstellen, indem Sie auf den von Blueqat bereitgestellten Mechanismus zurückgreifen. Sie können und Sie können sogar die vollständige "Run" -Methode implementieren, ohne an Bord zu kommen.
Jeder kann das Backend selbst erstellen und Sie können sogar Ihren eigenen Simulator mit der Blueqat-Oberfläche verwenden. Jeder, bitte versuchen Sie das Backend zu implementieren.
Recommended Posts