Zum Hauptinhalt springe

Iifüehrig i Bruchteil-Gates

Verbruchsschätzig: under 30 Sekunde uf emne Heron r2 Prozässor (HINWIIS: Das isch nume e Schätzig. Dini Loufziit cha anders sii.)

Hingergrund

Bruchteil-Gates uf IBM QPUs

Bruchteil-Gates si parametrisierti Quante-Gates wo s direkt Usführe vo willküerlich-Winkel Rotatione erloubet (innerhalb vo bestimmte Gränze), u dermit ischs nöd nötig si i mehreri Basis-Gates z'zerlege. Indem mer d native Wächselwürkige zwüschet de physikalische Qubits nutze, chöi Benutzer bestimmti Unitäri effiziänter uf Hardware implementiere.

IBM Quantum® Heron QPUs ungerstützet di folgende Bruchteil-Gates:

  • RZZ(θ)R_{ZZ}(\theta) für 0<θ<π/20 < \theta < \pi / 2
  • RX(θ)R_X(\theta) für alli reelli Wärt θ\theta

Dii Gates chöi sowohl d Tüüfi wie ou d Duur vo Quanteschaltkreis bedütend reduziere. Si si bsungers vorteilhaft i Awendige wo stark uf RZZR_{ZZ} u RXR_X buuet, wie Hamiltonsche Simulazioon, em Quantum Approximate Optimization Algorithm (QAOA), u Quante-Kernel Methode. I däm Tutorial konzentriere mer eus uf de Quante-Kernel aus praktischs Biischpil.

Iischränkige

Bruchteil-Gates si im Momänt e experimentelli Funkzioon u chömet mit e paar Iischränkige:

Bruchteil-Gates bruuchet e andere Workflow als de Standardaasatz. Das Tutorial erchlärt wie mer mit Bruchteil-Gates dür e praktischi Awendig schaffet.

Lueget emal zu de folgende für meh Details über Bruchteil-Gates.

Überblick

De Workflow fürs Verwände vo Bruchteil-Gates folgt generäll em Qiskit patterns Workflow. De wesentlich Ungerschid isch dass alli RZZ Winkel d Iischränkig 0<θπ/20 < \theta \leq \pi/2 müend erfülle. S git zwei Aasätz zum sicherstelle dass dii Bedingig erfüllt wird. Das Tutorial konzentriert sich uf u empfihlt de zwöiti Aasatz.

# Added by doQumentation — installs packages not in the Binder environment
!pip install -q qiskit-basis-constructor

1. Parameterwärt generiere wo d RZZ Winkeligschränkig erfüllet

Wänn du sicher bisch dass alli RZZ Winkel im gültige Beriich liget, chasch em Standardworkflow vo Qiskit patterns folge. I däm Fall gisch du eifach d Parameterwärt als Teil vomne PUB aa. De Workflow louft so:

pm = generate_preset_pass_manager(backend=backend, ...)
t_circuit = pm.run(circuit)
t_observable = observable.apply_layout(t_circuit.layout)
sampler.run([(t_circuit, parameter_values)])
estimator.run([(t_circuit, t_observable, parameter_values)])

Wänn du versuechsch e PUB aazgää wo e RZZ Gate mit emne Winkel usserhalb vom gültige Beriich enthaltet, überchunsch e Fählermäldig wie:

'The instruction rzz is supported only for angles in the range [0, pi/2], but an angle (20.0) outside of this range has been requested; via parameter value(s) γ[0]=10.0, substituted in parameter expression 2.0*γ[0].'

Zum dä Fähler z'vermiide, söttsch de zwöiti Aasatz überlegge wo unge beschribe wird.

2. Parameterwärt a Schaltkreis zuewiise vor de Transpilazioon

S qiskit-ibm-runtime Paket stellet e spezialisierte Transpiler-Pass bereit wo FoldRzzAngle heisst. Dä Pass transformiert Quanteschaltkreis so dass alli RZZ Winkel mit de RZZ Winkeligschränkig überiistimmet. Wänn du s Backend aa generate_preset_pass_manager oder transpile gisch, wändet Qiskit automatisch FoldRzzAngle uf d Quanteschaltkreis aa. Das verlanggt vo dir Parameterwärt a Quanteschaltkreis vor de Transpilazioon zuezwiise. De Workflow louft so:

pm = generate_preset_pass_manager(backend=backend, ...)
b_circuit = circuit.assign_parameters(parameter_values)
t_circuit = pm.run(b_circuit)
t_observable = observable.apply_layout(t_circuit.layout)
sampler.run([(t_circuit,)])
estimator.run([(t_circuit, t_observable)])

Merk dass dä Workflow meh Berächnigschoschte verursacht als de erscht Aasatz, wil s Parameterwärt a Quanteschaltkreis zuewiise u s Schpiichere vo de parameterbundene Schaltkreis lokal iischliesst. Zuesätzlich gits es bekannts Problem i Qiskit wo d Transformazioon vo RZZ Gates i bestimmte Szenarie fählschlaa cha. Für e Lösig, lueg mal im Troubleshooting Abschnitt naa. Das Tutorial zeigt wie mer Bruchteil-Gates über de zwöiti Aasatz bruuchet, dür es Biischpil wo vo de Quante-Kernel Methode inspiriert isch. Zum besser z'verstaa wo Quante-Kernels wohrschienlich nützlich si, empfähle mer s Läse vo Liu, Arunachalam & Temme (2021).

Du chasch ou s Quantum kernel training Tutorial u d Quantum kernels Lekzioon im Quantum machine learning Kurs uf IBM Quantum Learning düreschaffet.

Aaforderige

Bevor du mit däm Tutorial aafangsch, stell sicher dass du s Folgend installiert hesch:

  • Qiskit SDK v2.0 oder schpöter, mit visualization Ungerstützig
  • Qiskit Runtime v0.37 oder schpöter (pip install qiskit-ibm-runtime)
  • Qiskit Basis Constructor (pip install qiskit_basis_constructor)

Setup

import matplotlib.pyplot as plt
import numpy as np
from qiskit import QuantumCircuit, generate_preset_pass_manager
from qiskit.circuit import ParameterVector
from qiskit.circuit.library import unitary_overlap
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2

Bruchteil-Gates aktiviere u Basis-Gates überprüefe

Zum Bruchteil-Gates z'bruuche, chasch es Backend überchoo wo si ungerstützt, indem du d use_fractional_gates=True Opzioon setzisch. Wänn s Backend Bruchteil-Gates ungerstützt, gseesch rzz u rx under siine Basis-Gates ufglischtet.

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=133
) # backend should be a heron device or later
backend_name = backend.name
backend_c = service.backend(backend_name) # w/o fractional gates
backend_f = service.backend(
backend_name, use_fractional_gates=True
) # w/ fractional gates
print(f"Backend: {backend_name}")
print(f"No fractional gates: {backend_c.basis_gates}")
print(f"With fractional gates: {backend_f.basis_gates}")
if "rzz" not in backend_f.basis_gates:
print(f"Backend {backend_name} does not support fractional gates")
Backend: ibm_fez
No fractional gates: ['cz', 'id', 'rz', 'sx', 'x']
With fractional gates: ['cz', 'id', 'rx', 'rz', 'rzz', 'sx', 'x']

Workflow mit Bruchteil-Gates

Schritt 1: Klassischi Iigabe uf Quanteproblem abilde

Quante-Kernel Schaltkreis

I däm Abschnitt ungersüechet mer de Quante-Kernel Schaltkreis mit RZZ Gates zum de Workflow für Bruchteil-Gates vorzstelle.

Mer fanget aa indem mer e Quanteschaltkreis buuet zum einzelni Iiträg vo de Kernel-Matrix z'berächne. Das wird gmacht indem mer ZZ Feature-Map Schaltkreis mit emne unitary overlap kombiniere. D Kernel-Funkzioon nimmt Vektore im feature-mapped Ruum u git ihres inner Produkt als Iitrag vo de Kernel-Matrix zrugg: K(x,y)=Φ(x)Φ(y),K(x, y) = \langle \Phi(x) | \Phi(y) \rangle, wo Φ(x)|\Phi(x)\rangle de feature-mapped Quantezuestand darstellt.

Mer buuet manuell e ZZ Feature-Map Schaltkreis mit RZZ Gates. Obwohl Qiskit e iigbauti zz_feature_map bereitstellt, ungerstützt er zurziit kei RZZ Gates ab Qiskit v2.0.2 (lueg mal zum Issue).

Als nächschts berächnet mer d Kernel-Funkzioon für identischi Iigabe - zum Biischpil, K(x,x)=1K(x, x) = 1. Uf verruuschte Quantecomputer cha dä Wärt weniger als 1 sii wäge Ruusch. Es Resultat nööcher a 1 bedütet weniger Ruusch bi de Usführig. I däm Tutorial nänne mer dä Wärt d Fidelity, definiert als fidelity=K(x,x).\text{fidelity} = K(x, x).

optimization_level = 2
shots = 2000
reps = 3
rng = np.random.default_rng(seed=123)
def my_zz_feature_map(num_qubits: int, reps: int = 1) -> QuantumCircuit:
x = ParameterVector("x", num_qubits * reps)
qc = QuantumCircuit(num_qubits)
qc.h(range(num_qubits))
for k in range(reps):
K = k * num_qubits
for i in range(num_qubits):
qc.rz(x[i + K], i)
pairs = [(i, i + 1) for i in range(num_qubits - 1)]
for i, j in pairs[0::2] + pairs[1::2]:
qc.rzz((np.pi - x[i + K]) * (np.pi - x[j + K]), i, j)
return qc

def quantum_kernel(num_qubits: int, reps: int = 1) -> QuantumCircuit:
qc = my_zz_feature_map(num_qubits, reps=reps)
inner_product = unitary_overlap(qc, qc, "x", "y", insert_barrier=True)
inner_product.measure_all()
return inner_product

def random_parameters(inner_product: QuantumCircuit) -> np.ndarray:
return np.tile(rng.random(inner_product.num_parameters // 2), 2)

def fidelity(result) -> float:
ba = result.data.meas
return ba.get_int_counts().get(0, 0) / ba.num_shots

Quante-Kernel Schaltkreis u ihri entsprechendi Parameterwärt wärde für System mit 4 bis 40 Qubits generiert, u ihri Fidelities wärde dänaa bewertet.

qubits = list(range(4, 44, 4))
circuits = [quantum_kernel(i, reps=reps) for i in qubits]
params = [random_parameters(circ) for circ in circuits]

De Vier-Qubit Schaltkreis wird unge visualisiert.

circuits[0].draw("mpl", fold=-1)

Output of the previous code cell

Im Standard Qiskit patterns Workflow wärde Parameterwärt normalerwiis als Teil vomne PUB as Sampler oder Estimator Primitive übergää. Wänn mer aber es Backend bruuchet wo Bruchteil-Gates ungerstützt, müend dii Parameterwärt explizit vor de Transpilazioon zum Quanteschaltkreis zuegwiise wärde.

b_qc = [
circ.assign_parameters(param) for circ, param in zip(circuits, params)
]
b_qc[0].draw("mpl", fold=-1)

Output of the previous code cell

Schritt 2: Problem für Quante-Hardware Usführig optimiere

Mer transpilieret denn de Schaltkreis mit em Pass Manager naa em Standard Qiskit Pattern. Indem mer es Backend bereitstelle wo Bruchteil-Gates ungerstützt aa generate_preset_pass_manager, wird e spezialisierte Pass gnennt FoldRzzAngle automatisch iigfüegt. Dä Pass modifiziert de Schaltkreis zum mit de RZZ Winkeligschränkige überiizstimme. Als Resultat wärde RZZ Gates mit negative Wärt im vorherige Bild i positivi Wärt transformiert, u es paar zuesätzlichi X Gates wärde iigfüegt.

backend_f = service.backend(name=backend_name, use_fractional_gates=True)
# pm_f includes `FoldRzzAngle` pass
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_f
)
t_qc_f = pm_f.run(b_qc)
print(t_qc_f[0].count_ops())
t_qc_f[0].draw("mpl", fold=-1)
OrderedDict([('rz', 35), ('rzz', 18), ('x', 13), ('rx', 9), ('measure', 4), ('barrier', 2)])

Output of the previous code cell

Zum de Iifluss vo Bruchteil-Gates z'bewärte, evaluiere mer d Aazahl vo Nöd-Lokal Gates (CZ u RZZ für das Backend), zäme mit Schaltkreistüüfine u Duure, u vergliichet dii Metrike mit däne vomne Standardworkflow schpööter.

nnl_f = [qc.num_nonlocal_gates() for qc in t_qc_f]
depth_f = [qc.depth() for qc in t_qc_f]
duration_f = [
qc.estimate_duration(backend_f.target, unit="u") for qc in t_qc_f
]

Schritt 3: Mit Qiskit Primitives usführe

Mer lönd de transpiliert Schaltkreis mit em Backend wo Bruchteil-Gates ungerstützt.

sampler_f = SamplerV2(mode=backend_f)
sampler_f.options.dynamical_decoupling.enable = True
sampler_f.options.dynamical_decoupling.sequence_type = "XY4"
sampler_f.options.dynamical_decoupling.skip_reset_qubits = True
job = sampler_f.run(t_qc_f, shots=shots)
print(job.job_id())
d4bninsi51bc738j97eg

Schritt 4: Nachbearbeite u Resultat im gwünschte klassische Format zrüggää

Du chasch de Kernel-Funkzioonswärt K(x,x)K(x, x) überchoo indem du d Wohrschienlichkeit vom all-null Bitstring 00...00 im Output mässisch.

# job = service.job("d1obougt0npc73flhiag")
result = job.result()
fidelity_f = [fidelity(result=res) for res in result]
print(fidelity_f)
usage_f = job.usage()
[0.9005, 0.647, 0.3345, 0.355, 0.3315, 0.174, 0.1875, 0.149, 0.1175, 0.085]

Vergliich vo Workflow u Schaltkreis ohni Bruchteil-Gates

I däm Abschnitt präsentieret mer de Standard Qiskit patterns Workflow mit emne Backend wo kei Bruchteil-Gates ungerstützt. Indem du d transpilierte Schaltkreis vergliichsch, wirsch merke dass d Versioon mit Bruchteil-Gates (vom vorherige Abschnitt) kompakter isch als die ohni Bruchteil-Gates.

# step 1: map classical inputs to quantum problem
# `circuits` and `params` from the previous section are reused here
# step 2: optimize circuits
backend_c = service.backend(backend_name) # w/o fractional gates
pm_c = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_c
)
t_qc_c = pm_c.run(circuits)
print(t_qc_c[0].count_ops())
t_qc_c[0].draw("mpl", fold=-1)
OrderedDict([('rz', 130), ('sx', 80), ('cz', 36), ('measure', 4), ('barrier', 2)])

Output of the previous code cell

nnl_c = [qc.num_nonlocal_gates() for qc in t_qc_c]
depth_c = [qc.depth() for qc in t_qc_c]
duration_c = [
qc.estimate_duration(backend_c.target, unit="u") for qc in t_qc_c
]
# step 3: execute
sampler_c = SamplerV2(backend_c)
sampler_c.options.dynamical_decoupling.enable = True
sampler_c.options.dynamical_decoupling.sequence_type = "XY4"
sampler_c.options.dynamical_decoupling.skip_reset_qubits = True
job = sampler_c.run(pubs=zip(t_qc_c, params), shots=shots)
print(job.job_id())
d4bnirvnmdfs73ae3a2g
# step 4: post-processing
# job = service.job("d1obp8j3rr0s73bg4810")
result = job.result()
fidelity_c = [fidelity(res) for res in result]
print(fidelity_c)
usage_c = job.usage()
[0.6675, 0.5725, 0.098, 0.102, 0.065, 0.0235, 0.006, 0.0015, 0.0015, 0.002]

Vergliich vo Tüüfine u Fidelities

I däm Abschnitt vergliichet mer d Aazahl vo Nöd-Lokal Gates u d Fidelities zwüschet Schaltkreis mit u ohni Bruchteil-Gates. Das hebbt d potenzielli Vorteile vom Verwände vo Bruchteil-Gates bezüglich Usführigseffiziänz u Qualität füre.

plt.plot(qubits, depth_c, "-o", label="no fractional gates")
plt.plot(qubits, depth_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("depth")
plt.title("Comparison of depths")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x12bcaac50>

Output of the previous code cell

plt.plot(qubits, duration_c, "-o", label="no fractional gates")
plt.plot(qubits, duration_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("duration (µs)")
plt.title("Comparison of durations")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x12bdef310>

Output of the previous code cell

plt.plot(qubits, nnl_c, "-o", label="no fractional gates")
plt.plot(qubits, nnl_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("number of non-local gates")
plt.title("Comparison of numbers of non-local gates")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x12be8ac90>

Output of the previous code cell

plt.plot(qubits, fidelity_c, "-o", label="no fractional gates")
plt.plot(qubits, fidelity_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("fidelity")
plt.title("Comparison of fidelities")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x12bea8290>

Output of the previous code cell

Mer vergliichet d QPU Verbruchsziit mit u ohni Bruchteil-Gates. D Resultat i de folgende Zälle zeige dass d QPU Verbruchsziite fascht identisch si.

print(f"no fractional gates: {usage_c} seconds")
print(f"fractional gates: {usage_f} seconds")
no fractional gates: 7 seconds
fractional gates: 7 seconds

Fortgschrittens Thema: Nume frakzionelli RX Gates bruuche

D Notwendigkeit füre modifizierte Workflow bim Bruuche vo Bruchteil-Gates chunnt hauptsächlich vo de Iischränkig uf RZZ Gate Winkel. Wänn du aber nume d frakzionelli RX Gates bruuchsch u d frakzionelli RZZ Gates usschliessch, chasch wieder em Standard Qiskit patterns Workflow folge. Dä Aasatz cha trotzdem bedütendi Vorteile bringe, bsunders i Schaltkreis wo e grossi Aazahl vo RX Gates u U Gates enthaltet, indem er d Gsamtaazahl vo Gates reduziert u möglicherwiis d Performance verbesseret. I däm Abschnitt zeige mer wie du dini Schaltkreis optimiersch mit nume frakzionelle RX Gates, während du RZZ Gates usschliessch.

Zum das z'ungerstütze, stelle mer e Hilfsfunkzioon bereit wo dir erlaubt es bestimmts Basis-Gate i emne Target Objekt z'deaktiviere. Da bruuchet mer s zum RZZ Gates z'deaktiviere.

from qiskit.circuit.library import n_local
from qiskit.transpiler import Target
def remove_instruction_from_target(target: Target, gate_name: str) -> Target:
new_target = Target(
description=target.description,
num_qubits=target.num_qubits,
dt=target.dt,
granularity=target.granularity,
min_length=target.min_length,
pulse_alignment=target.pulse_alignment,
acquire_alignment=target.acquire_alignment,
qubit_properties=target.qubit_properties,
concurrent_measurements=target.concurrent_measurements,
)

for name, qarg_map in target.items():
if name == gate_name:
continue
instruction = target.operation_from_name(name)
if qarg_map == {None: None}:
qarg_map = None
new_target.add_instruction(instruction, qarg_map, name=name)
return new_target

Mer bruuchet e Schaltkreis wo us U, CZ, u RZZ Gates bestoht als Biischpil.

qc = n_local(3, "u", "cz", "linear", reps=1)
qc.rzz(1.1, 0, 1)
qc.draw("mpl")

Output of the previous code cell

Mer transpilieret zerscht de Schaltkreis für es Backend wo kei Bruchteil-Gates ungerstützt.

pm_c = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_c
)
t_qc = pm_c.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")
OrderedDict([('rz', 23), ('sx', 16), ('cz', 4)])

Output of the previous code cell

Denn transpilieret mer de gliichi Schaltkreis mit frakzionelle RX Gates, während mer RZZ Gates usschliesset. Das füehrt zu ere chliine Redukzioon i de Gsamtgatezahl, dank de effiziäntere Implementazioon vo de RX Gates.

backend_f = service.backend(backend_name, use_fractional_gates=True)
target = remove_instruction_from_target(backend_f.target, "rzz")
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level,
target=target,
)
t_qc = pm_f.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")
OrderedDict([('rz', 22), ('sx', 14), ('cz', 4), ('rx', 1)])

Output of the previous code cell

U Gates mit frakzionelle RX Gates optimiere

I däm Abschnitt zeige mer wie mer U Gates mit frakzionelle RX Gates optimiere, ufbuuend uf em gliiche Schaltkreis wo im vorherige Abschnitt vorgstellt worde isch.

Du müesch s qiskit-basis-constructor Paket für dä Abschnitt installiere. Das isch e Beta-Versioon vomne neue Transpilazioons-Plugin für Qiskit, wo möglicherwiis ide Zuekunft i Qiskit integriert wird.

# %pip install qiskit-basis-constructor
from qiskit.circuit.library import UGate
from qiskit_basis_constructor import DEFAULT_EQUIVALENCE_LIBRARY

Mer transpilieret de Schaltkreis nume mit frakzionelle RX Gates, u schliesset RZZ Gates us. Indem mer e benutzerdefinierti Zerleggsregel iifüeret, wie unge zeigt, chöi mer d Aazahl vo Single-Qubit Gates reduziere wo nötig si zum es U Gate z'implementiere.

Dii Funkzioon wird im Momänt i däm GitHub Issue diskutiert.

# special decomposition rule for UGate
x = ParameterVector("x", 3)
zxz = QuantumCircuit(1)
zxz.rz(x[2] - np.pi / 2, 0)
zxz.rx(x[0], 0)
zxz.rz(x[1] + np.pi / 2, 0)
DEFAULT_EQUIVALENCE_LIBRARY.add_equivalence(UGate(x[0], x[1], x[2]), zxz)

Als nächschts wände mer de Transpiler aa mit constructor-beta Übersätzig bereitgstellt vom qiskit-basis-constructor Paket. Als Resultat wird d Gsamtaazahl vo Gates reduziert vergliche mit de vorherige Transpilazioon.

pm_f = generate_preset_pass_manager(
optimization_level=optimization_level,
target=target,
translation_method="constructor-beta",
)
t_qc = pm_f.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")
OrderedDict([('rz', 16), ('rx', 9), ('cz', 4)])

Output of the previous code cell

Troubleshooting

Problem: Ungültigi RZZ Winkel chöi naa de Transpilazioon bliibe

Ab Qiskit v2.0.3 gits bekannti Probleem wo RZZ Gates mit ungültige Winkel i de Schaltkreis bliibe chöi ou naa de Transpilazioon. S Problem tritt normalerwiis under de folgende Bedingige uf.

Fählschlag wänn du d target Opzioon mit generate_preset_pass_manager oder transpiler bruuchsch

Wänn d target Opzioon mit generate_preset_pass_manager oder transpiler bruucht wird, wird de spezialisiert Transpiler-Pass FoldRzzAngle nöd ufgruefe. Zum e korrekti Behandlig vo RZZ Winkel für Bruchteil-Gates sicherzstelle, empfähle mer immer d backend Opzioon z'bruuche. Lueg mal zu däm Issue für meh Details.

Fählschlag wänn Schaltkreis bestimmti Gates enthaltet

Wänn din Schaltkreis bestimmti Gates wie XXPlusYYGate enthaltet, cha de Qiskit Transpiler RZZ Gates mit ungültige Winkel generiere. Wänn du uf das Problem stössch, lueg mal zu däm GitHub Issue für e Lösig.

Tutorial-Umfroog

Bitte nimm dir churz Ziit für dii churzi Umfroog zum Feedback zu däm Tutorial z'gää. Dini Iisichte hälfet eus öises Inhaltaagebott u Benutzererfahrig z'verbessere.

Link zur Umfroog