NiFi flow configuration guide
This is a hands-on, click-by-click guide to building and running every processor
in nifi_extensions/ on the NiFi canvas. It is organized as
24 flows, ordered so you can work through them one at a time and exercise
every component at least once. Standalone-friendly processors get their own
small flow; processors that only make sense chained together (the three *VQE
pipelines, Cirq's oracle+operator pair, the PennyLane QML lane, the comparison /
assertion pair, …) are grouped into a single combined flow instead of one flow
per processor — per the "category, not name" rule in
CREATING_PROCESSORS.md.
Companion docs: CREATING_PROCESSORS.md (how processors are built), DATA_DRIVEN_TESTING.md (the attribute-externalization contract used in Flow 24), and The VQE flow uses the shared Hamiltonian and ansatz attribute contracts.
Before you start
- Start NiFi from your local installation and open the canvas in
a browser (
https://localhost:8443/nifior whatever yournifi.propertiessets). - Reports directory — most flows end in
QuanifiReport/QiskitCircuitReport/QiskitStatevectorSimulator, which append HTML "run cards" to files underreports/. Make sure that directory (andreports/tmp/for the comparison processors' pairing state) exists and is writable by the NiFi process. Open the resulting.htmlfiles directly in a browser and refresh after each run — new cards are appended at the top. - General recipe (applies to every flow below):
- Drag the Processor icon onto the canvas, search for the class name
(e.g.
QiskitGroverCircuit), add it. - Double-click it → Properties tab → set the values from the tables below (leave anything not mentioned at its default).
- Drag from one processor's edge to the next to create a Connection;
in the dialog, tick the relationship(s) named in "Wire" below
(almost always
success). - For every relationship a processor can emit that you are not wiring
to something downstream (typically
failure, and the unused side ofpass/failorsuccess/waiting), open the processor's Settings tab (or the connection dialog) and tick automatically terminate — otherwise NiFi refuses to start it. - Select every processor in the flow and press the ▶ Start button (or right-click → Start).
- To inspect a FlowFile, right-click the connection → List queue → the little "i" icon → View (content) / Attributes tab.
- First placement of a processor triggers NiFi to build an isolated venv
and install
ProcessorDetails.dependencies— this can take a minute; watch the bulletin board (top-right bell icon) for errors.
Qiskit
Flow 1 — Smoke test: QiskitQuantumHelloWorld
Components: QiskitQuantumHelloWorld
What it proves: NiFi can load the processor, its isolated venv has Qiskit,
and you can read FlowFile content/attributes. No upstream wiring needed.
- Add
QiskitQuantumHelloWorld. - Properties: Qubit Count =
2(default; try3later to see a 3-letter GHZ string). - Add a
GenerateFlowFileprocessor upstream (any standard NiFi processor; content/schedule don't matter — it's just the trigger) → connect itssuccesstoQiskitQuantumHelloWorld. - Wire
QiskitQuantumHelloWorld'ssuccessto a funnel (or any processor you auto-terminate) so you can list the queue. - Start everything. List the queue on the final connection: content is the
measured GHZ bitstring (e.g.
00or11); attributes carryquantum.bitstringandquantum.qubits.
Flow 2 — Qiskit Grover circuit → simulator → circuit report
Components: QiskitGroverCircuit → QiskitAerSimulator → QiskitCircuitReport
What it proves: the Tier-1 "circuit builder → simulator → report" shape
that most Qiskit/Cirq flows below reuse, plus the Grover-specific HTML report.
QiskitGroverCircuit| Property | Value | |---|---| | Marked State |11| | Num Iterations |1| | Insert Barriers |false| | Output Format |qasm3|
Wire success → QiskitAerSimulator.
QiskitAerSimulator| Property | Value | |---|---| | Shots |1024| | Noise Model |none(trydepolarizingon a second run to see the noise panel) |
Leave the noise/backend properties at default for an ideal run. Wire
success → QiskitCircuitReport.
QiskitCircuitReport| Property | Value | |---|---| | Reports Directory |reports| | Flow Name |qiskit-grover|
Auto-terminate success (it's the terminal node).
- Start all three. Open
reports/qiskit-grover.html— you should see a card with the circuit diagram (QASM3), the marked state/iteration count, and a measurement-count bar chart dominated by11.
Flow 3 — Qiskit Hadamard transform
Components: QiskitHadamardTransform → QiskitAerSimulator → QuanifiReport
What it proves: standalone-vs-compose mode (run it bare first, then chained
onto an existing circuit) and the generic QuanifiReport "simulation" card.
QiskitHadamardTransform| Property | Value | |---|---| | Qubit Count |3| | Output Format |qasm3|
Feed it from a GenerateFlowFile with empty content (standalone mode —
it builds a fresh 3-qubit H⊗3 circuit). Wire success → QiskitAerSimulator.
2. QiskitAerSimulator — Shots 1024, Noise Model none. Wire success
→ QuanifiReport.
3. QuanifiReport — Reports Directory (default), Flow Name = qiskit-hadamard.
Auto-terminate success.
4. Start, then open qiskit-hadamard.html. Counts should be roughly uniform
across all 8 three-bit strings (uniform superposition).
5. Try compose mode: place a fresh QiskitGroverCircuit (Flow 2 settings)
before QiskitHadamardTransform instead of GenerateFlowFile — now it
appends H to every qubit of the Grover circuit it receives (it detects the
incoming circuit.format and non-empty content).
Flow 4 — Qiskit QFT circuit
Components: QiskitQFTCircuit → QiskitAerSimulator → QuanifiReport
QiskitQFTCircuit| Property | Value | |---|---| | Qubit Count |3| | Inverse |false| | Approximation Degree |0| | Do Swaps |true| | Insert Barriers |false| | Output Format |qasm3|
Feed from GenerateFlowFile (empty content → standalone mode, fresh 3-qubit
QFT). Wire success → QiskitAerSimulator.
2. QiskitAerSimulator — Shots 1024. Wire success → QuanifiReport.
3. QuanifiReport — Flow Name = qiskit-qft. Auto-terminate success.
4. Start; open qiskit-qft.html. Then flip Inverse to true and re-run to
compare QFT vs QFT† — note circuit.qft_inverse in the Circuit Attributes
table changes accordingly.
Flow 5 — Qiskit Phase Estimation
Components: QiskitPhaseEstimation → QiskitAerSimulator → QuanifiReport
QiskitPhaseEstimation| Property | Value | |---|---| | Phase Register Size |3| | Builtin Unitary |T| | Insert Barriers |false| | Output Format |qasm3|
Feed from GenerateFlowFile with empty content (standalone mode — it
builds a demo QPE estimating the T gate's eigenphase 1/8 with the
eigenstate |1⟩ prepared). Wire success → QiskitAerSimulator.
2. QiskitAerSimulator — Shots 1024. Wire success → QuanifiReport.
3. QuanifiReport — Flow Name = qiskit-qpe. Auto-terminate success.
4. Start; open qiskit-qpe.html. The Decoded Result panel reads the phase
register off sim.top_result and shows the answer directly: with
Builtin Unitary = T and a 3-qubit phase register, Estimated phase (φ) =
0.1250 (= 1/8), readout bits 001. (sim.top_result itself is the full
4-qubit string 1100 — the 4th qubit is the |1⟩ eigenstate; the decoder
extracts just the phase bits.) Switch Builtin Unitary to S (expect
1/4, bits 010) or Z (expect 1/2, bits 100) and re-run to see the
decoded phase shift.
Note: the circuit builder no longer self-measures — measurement is the
simulator's job. Earlier builds called measure_all() in both stages, which
produced a garbled doubled readout like 1100 1100.
Flow 6 — Qiskit Amplitude Amplification
Components: QiskitAmplitudeAmplification → QiskitAerSimulator → QuanifiReport
QiskitAmplitudeAmplification| Property | Value | |---|---| | Marked State |11| | Num Iterations |1| | Insert Barriers |false| | Output Format |qasm3|
Feed from GenerateFlowFile with empty content — standalone mode: it
builds its own phase oracle for Marked State and amplifies it. (You can
later feed it a circuit.format-bearing oracle, e.g. from
QiskitGroverCircuit with Num Iterations = 0, to exercise compose
mode instead — circuit.mode flips from standalone to compose and
circuit.marked_state is no longer set.) Wire success → QiskitAerSimulator.
2. QiskitAerSimulator — Shots 1024. Wire success → QuanifiReport.
3. QuanifiReport — Flow Name = qiskit-amplitude-amplification.
Auto-terminate success.
4. Start; open the report. circuit.mode = standalone and
circuit.marked_state = 11 should appear in the Circuit Attributes table,
and the histogram should peak at 11.
Flow 7 — Qiskit State Preparation → Statevector Simulator
Components: QiskitStatePreparation → QiskitStatevectorSimulator
What it proves: the exact-statevector lane (no shots/noise — it's its own
self-reporting terminal node, unlike the shot-based simulators above).
QiskitStatePreparation| Property | Value | |---|---| | State Type |ghz| | Qubit Count |3| | Output Format |qasm3|
(Target Basis State / Amplitudes are ignored for ghz; try basis with
Target Basis State = 3 or custom with Amplitudes = [1,0,0,1] on later
runs.) Feed from GenerateFlowFile. Wire success →
QiskitStatevectorSimulator.
2. QiskitStatevectorSimulator
| Property | Value |
|---|---|
| Reports Directory | reports |
| Flow Name | qiskit-statevector |
| Probability Threshold | 0.001 |
| Max States | 32 |
Auto-terminate success — there is no downstream QuanifiReport here; this
processor writes its own HTML directly.
3. Start both. Open qiskit-statevector-html (file name is
{flow_name}-statevector.html, i.e.
reports/qiskit-statevector-statevector.html). For a 3-qubit GHZ state you
should see exactly two rows, 000 and 111, each at probability ≈ 0.5,
with amplitude/phase columns.
Flow 8 — Qiskit Grover Search (all-in-one)
Components: QiskitGroverSearch → QuanifiReport
What it proves: the "all-in-one / solver" category — a single processor
that builds and simulates, emitting both circuit.*-style and grover.*
algorithm-specific attributes (no separate simulator needed).
QiskitGroverSearch| Property | Value | |---|---| | Marked State |101| | Num Iterations |2| | Shots |1024| | Insert Barriers |false|
Feed from GenerateFlowFile. Wire success → QuanifiReport.
2. QuanifiReport — Flow Name = qiskit-grover-search. Note: this
processor does not set report.type, so QuanifiReport falls back to
its _default card (raw JSON content + an "All Attributes" table) rather
than the simulation layout — that's expected; look for grover.top_result
and grover.top_probability in the attributes table, which should show
101 dominating. Auto-terminate success.
3. Start both and inspect the report.
Flow 9 — Qiskit VQE pipeline (one combined flow)
Components: QiskitHamiltonian → QiskitAnsatz → QiskitVQE → QuanifiReport
Why one flow: VQE
is decomposed — the Hamiltonian and ansatz stages are reusable building
blocks, but QiskitVQE is a strict solver that refuses to run without both
of them on the FlowFile (failure + vqe.error otherwise). Testing any one in
isolation from the others isn't meaningful, so they're documented as a single
chain.
QiskitHamiltonian| Property | Value | |---|---| | Hamiltonian |Z0 + Z1 + 0.5 X0 X1| | Num Qubits |0(auto-derive → 2) |
Feed from GenerateFlowFile. Wire success → QiskitAnsatz; auto-terminate
failure (only triggers on a DSL parse error).
2. QiskitAnsatz
| Property | Value |
|---|---|
| Ansatz Type | efficient_su2 |
| Reps | 2 |
| Entanglement | full |
| Num Qubits | 2 (ignored — chain mode activates because the incoming
FlowFile already carries hamiltonian.format + hamiltonian.num_qubits, so
the qubit count comes from the Hamiltonian and its content passes through
untouched) |
Wire success → QiskitVQE; auto-terminate failure.
3. QiskitVQE
| Property | Value |
|---|---|
| Optimizer | COBYLA |
| Max Iterations | 100 |
| Shots | 1024 |
| Initial Parameters | random |
This is the strict solver — it requires hamiltonian.format =
sparse_pauli_op_json and ansatz.qpy_b64 on the FlowFile (both supplied
by the two upstream stages) and routes to failure/vqe.error if either is
missing. Wire success → QuanifiReport; auto-terminate failure.
4. QuanifiReport — Flow Name = qiskit-vqe. Auto-terminate success.
5. Start all four. Open qiskit-vqe.html; look for vqe.optimal_value,
vqe.converged, vqe.num_iterations, and the trained circuit's measurement
histogram (it uses the simulation card layout because QiskitVQE sets
report.type = simulation).
6. To see the strict-solver failure path, temporarily disconnect
QiskitHamiltonian → QiskitAnsatz and feed QiskitVQE directly from
GenerateFlowFile: it routes to failure with vqe.error = "no Hamiltonian
on the FlowFile...". Reconnect afterwards.
Cirq
Flow 10 — Cirq Grover circuit → simulator
Components: CirqGroverCircuit → CirqSimulator → QuanifiReport
What it proves: the Cirq equivalent of Flow 2's shape, plus
circuit.svg/Cirq-JSON content (which QuanifiReport prefers over QASM).
CirqGroverCircuit| Property | Value | |---|---| | Marked State |11| | Num Iterations |1| | Insert Barriers |false| | Output Format |cirq_json|
Feed from GenerateFlowFile. Wire success → CirqSimulator.
2. CirqSimulator
| Property | Value |
|---|---|
| Shots | 1024 |
| Noise Model | none |
| Error Probability | 0.01 (ignored when Noise Model = none) |
| Damping Gamma | 0.05 (ignored unless amplitude_damp) |
| Readout Error Rate | 0.0 |
Wire success → QuanifiReport; auto-terminate failure (only fires for
an unsupported circuit.format).
3. QuanifiReport — Flow Name = cirq-grover. Auto-terminate success.
4. Start; open cirq-grover.html. The card should show the SVG circuit
diagram (Cirq renders circuit.svg, preferred over QASM) and a histogram
peaking at 11. Re-run with Output Format = qasm2 on
CirqGroverCircuit to see the alternate serialisation path (and
circuit.qasm2 show up instead).
Flow 11 — Cirq Phase Oracle + Grover Operator (one combined flow)
Components: CirqPhaseOracle → CirqGroverOperator → CirqSimulator → QuanifiReport
Why one flow: CirqGroverOperator is explicitly designed to consume the
oracle that CirqPhaseOracle produces (it prepends the H⊗n layer and applies
oracle+diffuser); neither is a meaningful standalone test on its own.
CirqPhaseOracle| Property | Value | |---|---| | Marked State |101| | Insert Barriers |false| | Output Format |cirq_json|
Feed from GenerateFlowFile with empty content (standalone mode — bare
oracle on a fresh 3-qubit register). Wire success → CirqGroverOperator.
2. CirqGroverOperator
| Property | Value |
|---|---|
| Num Iterations | 2 |
| Insert Barriers | false |
| Output Format | cirq_json |
It reads the oracle off the FlowFile content (per circuit.format,
defaulting to cirq_json), prepends H⊗n, and applies oracle+diffuser
Num Iterations times. Wire success → CirqSimulator.
3. CirqSimulator — Shots 1024, Noise Model none. Wire success →
QuanifiReport; auto-terminate failure.
4. QuanifiReport — Flow Name = cirq-grover-operator. Auto-terminate
success.
5. Start all four; open the report. circuit.marked_state = 101 should be
forwarded all the way through (both downstream processors re-emit it when
present), and the histogram should peak at 101.
Flow 12 — Cirq Hadamard transform
Components: CirqHadamardTransform → CirqSimulator → QuanifiReport
CirqHadamardTransform— Qubit Count3, Output Formatcirq_json. Feed fromGenerateFlowFilewith empty content (standalone — fresh H⊗3). Wiresuccess→CirqSimulator.CirqSimulator— Shots1024. Wiresuccess→QuanifiReport.QuanifiReport— Flow Name =cirq-hadamard. Auto-terminatesuccess.- Start; counts should be roughly uniform over all 8 three-bit strings.
- Compose mode: insert a
CirqGroverCircuit(Flow 10 settings) before it — it now appendsHto the existing circuit's qubits and forwardscircuit.marked_state.
Flow 13 — Cirq QFT circuit
Components: CirqQFTCircuit → CirqSimulator → QuanifiReport
CirqQFTCircuit| Property | Value | |---|---| | Qubit Count |3| | Inverse |false| | Do Swaps |true| | Insert Barriers |false| | Output Format |cirq_json|
Feed from GenerateFlowFile (empty content → standalone). Wire success →
CirqSimulator.
2. CirqSimulator — Shots 1024. Wire success → QuanifiReport.
3. QuanifiReport — Flow Name = cirq-qft. Auto-terminate success.
4. Start; toggle Inverse between runs and watch circuit.qft_inverse flip
in the report's attribute table.
Flow 14 — Cirq Phase Estimation
Components: CirqPhaseEstimation → CirqSimulator → QuanifiReport
CirqPhaseEstimation| Property | Value | |---|---| | Phase Register Size |3| | Builtin Unitary |T| | Insert Barriers |false| | Output Format |cirq_json|
Feed from GenerateFlowFile with empty content (standalone — builtin T
gate, expected dominant readout 001). Wire success → CirqSimulator.
2. CirqSimulator — Shots 1024. Wire success → QuanifiReport.
3. QuanifiReport — Flow Name = cirq-qpe. Auto-terminate success.
4. Start; open the report and confirm the 001/010/100 readouts as you
cycle Builtin Unitary through T/S/Z. You can also feed it a
circuit from CirqGroverCircuit upstream to exercise compose mode
(the incoming circuit becomes the unitary; circuit.qpe_builtin then reads
compose).
Flow 15 — Cirq VQE pipeline (one combined flow)
Components: CirqHamiltonian → CirqAnsatz → CirqVQE → QuanifiReport
Why one flow: same rationale as Flow 9 — CirqVQE is a strict solver that
requires both upstream stages.
CirqHamiltonian— HamiltonianZ0 + Z1 + 0.5 X0 X1, Num Qubits0. Feed fromGenerateFlowFile. Wiresuccess→CirqAnsatz; auto-terminatefailure.CirqAnsatz| Property | Value | |---|---| | Ansatz Type |efficient_su2| | Reps |2| | Entanglement |full| | Num Qubits |2(ignored — chain mode picks uphamiltonian.num_qubits) |
Wire success → CirqVQE; auto-terminate failure.
3. CirqVQE
| Property | Value |
|---|---|
| Optimizer | COBYLA |
| Max Iterations | 100 |
| Shots | 1024 |
| Initial Parameters | random |
Strict solver: requires hamiltonian.format = sparse_pauli_op_json (content
parsed via wire_to_terms) and ansatz.cirq_json on the FlowFile. Wire
success → QuanifiReport; auto-terminate failure.
4. QuanifiReport — Flow Name = cirq-vqe. Auto-terminate success.
5. Start; open cirq-vqe.html. Compare vqe.optimal_value /
vqe.elapsed_seconds against the Qiskit run from Flow 9 — same Hamiltonian
expression, two independently hand-rolled solvers (CirqVQE is
cirq.Simulator + scipy.optimize by hand vs. qiskit_algorithms.VQE).
Qrisp
Flow 16 — Qrisp Grover Search (all-in-one)
Components: QrispGroverSearch → QuanifiReport
QrispGroverSearch| Property | Value | |---|---| | Marked State |11| | Num Iterations |0(let Qrisp pick the optimal count automatically) | | Shots |1024|
Feed from GenerateFlowFile. Wire success → QuanifiReport.
2. QuanifiReport — Flow Name = qrisp-grover. Note: like
QiskitGroverSearch, this all-in-one does not set report.type, so you
get the _default card; check grover.framework = qrisp,
sim.top_result, sim.top_probability and circuit.num_iterations
(= "auto" when Num Iterations = 0) in the attribute table. Auto-terminate
success.
3. Start both; the content is a JSON probability distribution (Qrisp returns
probabilities, not raw counts) peaking at 11.
Flow 17 — Qrisp QFT circuit → simulator
Components: QrispQFTCircuit → QrispSimulator → QuanifiReport
Note: QrispQFTCircuit always emits circuit.format = qasm2 — the only
format QrispSimulator accepts (anything else routes it to failure with
sim.error), so this pairing is the path of least resistance.
QrispQFTCircuit| Property | Value | |---|---| | Qubit Count |3| | Inverse |false| | Do Swaps |true|
Feed from GenerateFlowFile. Wire success → QrispSimulator.
2. QrispSimulator — Shots 1024. Wire success → QuanifiReport;
auto-terminate failure (fires only on an unsupported circuit.format —
won't happen here since the upstream always emits qasm2).
3. QuanifiReport — Flow Name = qrisp-qft. Auto-terminate success.
4. Start; open the report and toggle Inverse, watching
circuit.qft_inverse change.
Flow 18 — Qrisp Phase Estimation (all-in-one)
Components: QrispPhaseEstimation → QuanifiReport
Note: unlike its Qiskit/Cirq counterparts, this processor has no compose
mode — it always builds its own demo QPE for a builtin unitary and emits its
own qpe.* result attributes plus a probability-distribution content (no
simulator needed downstream).
QrispPhaseEstimation| Property | Value | |---|---| | Phase Register Size |3| | Builtin Unitary |T| | Shots |1024|
Feed from GenerateFlowFile. Wire success → QuanifiReport.
2. QuanifiReport — Flow Name = qrisp-qpe. No report.type is set here
either, so expect the _default card; look at qpe.top_phase,
qpe.top_probability, qpe.precision (≈ 0.125 for T) in the attribute
table. Auto-terminate success.
3. Start; cycle Builtin Unitary through T/S/Z (expected qpe.top_phase
≈ 0.125/0.25/0.5) between runs.
Flow 19 — Qrisp VQE pipeline (one combined flow)
Components: QrispHamiltonian → QrispAnsatz → QrispVQE → QuanifiReport
Why one flow: same strict-solver rationale as Flows 9/15. Note Qrisp's
ansatz "carrier" is a spec, not a circuit (ansatz.format = qrisp_spec) —
QrispVQE rebuilds the callable ansatz_function(qv, theta) from
ansatz.type/ansatz.reps/ansatz.entanglement via QrispAnsatz's
_build_ansatz_function.
QrispHamiltonian— HamiltonianZ0 + Z1 + 0.5 X0 X1, Num Qubits0. Feed fromGenerateFlowFile. Wiresuccess→QrispAnsatz; auto-terminatefailure.QrispAnsatz| Property | Value | |---|---| | Ansatz Type |efficient_su2| | Reps |2| | Entanglement |full| | Num Qubits |2(ignored — chain mode readshamiltonian.num_qubits) |
Wire success → QrispVQE. (No failure relationship is declared on this
processor — nothing to auto-terminate.)
3. QrispVQE
| Property | Value |
|---|---|
| Optimizer | COBYLA |
| Max Iterations | 100 |
| Shots | 1024 |
| Initial Parameters | random |
Strict solver: requires hamiltonian.format = sparse_pauli_op_json and
ansatz.format = qrisp_spec. Wire success → QuanifiReport;
auto-terminate failure.
4. QuanifiReport — Flow Name = qrisp-vqe. Auto-terminate success.
5. Start all four; open qrisp-vqe.html. QrispVQE calls Qrisp's first-class
VQEProblem.run(...) — compare vqe.optimal_value /
vqe.elapsed_seconds against Flows 9 and 15 for a three-way cross-framework
comparison on the identical Hamiltonian expression and ansatz spec.
PennyLane (QML lane)
Flow 20 — Feature embedding → variational ansatz → expectation (QNode chain)
Components: PennylaneFeatureEmbedding → PennylaneVariationalAnsatz →
PennylaneExpectation → QuanifiReport
Why one flow: these three chain end to end and only make sense as a
pipeline — PennyLane is QNode-centric (its native output is expectation values
/ probabilities, not raw shot counts), and circuit.format = qasm2 is the glue
between every stage.
PennylaneFeatureEmbedding| Property | Value | |---|---| | Embedding |angle| | Features |[0.1, 0.5, 0.9](used as a fallback only — feed real content below) | | Rotation |Y|
Feed from a GenerateFlowFile whose content is a JSON array of numbers,
e.g. [0.2, 0.4, 0.6] (this is preferred over the Features property
fallback — set GenerateFlowFile's "Custom Text" / content accordingly and
mime.type = application/json if you like). One qubit is allocated per
feature → 3 qubits here. Wire success → PennylaneVariationalAnsatz;
auto-terminate failure.
2. PennylaneVariationalAnsatz
| Property | Value |
|---|---|
| Ansatz | strongly_entangling |
| Num Qubits | 3 (ignored — compose mode activates because the
incoming circuit.format = qasm2; qubit count is taken from the embedding
circuit) |
| Num Layers | 2 |
| Weight Seed | 42 |
Wire success → PennylaneExpectation; auto-terminate failure.
3. PennylaneExpectation
| Property | Value |
|---|---|
| Observable | Z |
| Shots | (leave blank for an exact analytic result, or set e.g. 1024 to sample) |
This is the PennyLane execution lane (sim.framework = pennylane) —
strictly requires circuit.format = qasm2 (both upstream processors emit
it). Wire success → QuanifiReport; auto-terminate failure.
4. QuanifiReport — Flow Name = pennylane-qml. It uses the simulation
card layout (report.type = simulation). Auto-terminate success.
5. Start all four. Open pennylane-qml.html: instead of plain shot counts you
should see sim.observable = Z, sim.expectations (a per-qubit ⟨Z⟩ JSON
map), plus the usual sim.top_result/sim.top_probability derived from the
probability distribution. Try Observable = X or Y to see the
expectation values change, and compare standalone vs. compose mode on
PennylaneVariationalAnsatz by disconnecting the embedding stage and
feeding it directly from GenerateFlowFile (it then builds a fresh
Num Qubits-sized ansatz with no embedding in front).
Flow 21 — PennyLane dataset loader (standalone generator)
Components: PennylaneDatasetLoader
Why separate: it's a source processor. Its natural consumer is the
training lane: wire it straight into PennylaneVariationalClassifier
(Flow 33), which reads the whole record array from the FlowFile content. To
feed the circuit lane instead (Flow 20), bridge one record's features
array into FlowFile content with standard NiFi SplitJson +
EvaluateJsonPath, the same pattern Flow 24 uses for test cases.
PennylaneDatasetLoader| Property | Value | |---|---| | Mode |synthetic| | Num Samples |20| | Num Features |3| | Class Separation |2.0| | Seed |0|
Feed from GenerateFlowFile. Wire success to a funnel/auto-terminated
processor; auto-terminate failure.
2. Start, list the queue: content is a JSON array of 20
{"features": [...], "label": 0|1} records; attributes show
dataset.count = 20, dataset.num_features = 3, dataset.num_classes = 2,
dataset.framework = pennylane.
3. Switch Mode to inline and set Records to a literal JSON array
(e.g. [{"features":[0,0,0],"label":0},{"features":[1,1,1],"label":1}]) to
exercise the second code path — dataset.mode should now read inline and
dataset.count = 2.
Cross-framework comparison & equivalence
Flow 22 — Unitary equivalence: Qiskit vs. Cirq Grover circuit
Components: QuanifiUnitary (×2) → QuantumUnitaryComparison → QuanifiReport
What it proves: that two circuit-producing processors from different
frameworks build mathematically equivalent unitaries (up to global phase) —
exercising QuanifiUnitary's Qiskit→Cirq qubit-order normalisation
(reverse_bits()) and the pairing/state-persistence machinery in
QuantumUnitaryComparison.
- Branch A — reuse/duplicate the
QiskitGroverCircuitfrom Flow 2 with Marked State =11, Num Iterations =1, Output Format =qasm3, and set its Num Iterations property to0so the resulting circuit is just the oracle+state-prep layer (a deterministic, unmeasured circuit — the formQuanifiUnitaryneeds). Wiresuccess→ aQuanifiUnitaryinstance ("Unitary A"). - Branch B — a
CirqGroverCircuitwith the sameMarked State = 11,Num Iterations = 0,Output Format = cirq_json. Wiresuccess→ a secondQuanifiUnitaryinstance ("Unitary B"). - Both
QuanifiUnitaryinstances | Property | Value | |---|---| | Max Qubits |12|
Each requires circuit.format on the FlowFile (fails clearly with
unitary.error_type = missing_circuit_format_attribute otherwise — both
branches supply it). Wire each success → the same
QuantumUnitaryComparison instance; auto-terminate failure on both.
4. QuantumUnitaryComparison
| Property | Value |
|---|---|
| State Directory | reports/tmp/quanifi_unitary_state |
| Comparison Label | grover-2q-equiv |
| Framework Label | ${unitary.source_format} (default — auto-detects qasm3 vs cirq_json) |
| Tolerance | 1e-9 |
It pairs FlowFiles by Comparison Label (both branches must use the same
value/instance) and persists the first arrival to State Directory until
the second shows up. Wire success → QuanifiReport. The failure
relationship covers both "first of pair, waiting" and real errors
(not_a_unitary_flowfile, json_parse_failure, state_load_failure) — per
its docstring, just auto-terminate it (you can inspect equiv.status =
waiting on those FlowFiles if curious before terminating).
5. QuanifiReport — Flow Name = unitary-equivalence. Note: this
processor doesn't set report.type, so it lands in the _default card —
look for equiv.equivalent = true, equiv.process_fidelity ≈ 1.0000000000,
and equiv.global_phase_degrees in the attributes table. Auto-terminate
success.
6. Start everything, run both branches once (so a pair completes), then
open the report. equiv.equivalent should read true — Qiskit and Cirq
build the same Grover oracle+state-prep unitary (modulo the
reverse_bits() qubit-order fix QuanifiUnitary applies to Qiskit
circuits).
Flow 23 — Distribution comparison + assertion gate (one combined flow)
Components: QuantumDistributionComparison → QuantumAssertion → QuanifiReport
Why one flow: QuantumAssertion is explicitly designed to sit immediately
downstream of QuantumDistributionComparison (it reads compare.* attributes
and is the natural pass/fail gate on top of it) — testing it without a real
comparison upstream means hand-crafting compare.* attributes yourself.
- Branch A —
QiskitGroverCircuit(Marked State = 11,Num Iterations = 1,Output Format = qasm3) →QiskitAerSimulator(Shots = 1024). This is the same shape as Flow 2's first two stages — you can reuse those instances and fan theirsuccessout to a second destination. - Branch B —
CirqGroverCircuit(Marked State = 11,Num Iterations = 1,Output Format = cirq_json) →CirqSimulator(Shots = 1024). Likewise reusable from Flow 10. - Wire both branches' final
successinto a singleQuantumDistributionComparisoninstance. | Property | Value | |---|---| | Reports Directory |reports| | Flow Name |grover-qiskit-vs-cirq| | State Directory |reports/tmp/quanifi_compare_state| | Comparison Label |grover-comparison| | Framework Label |${grover.framework}(default; falls back to auto-labelsrun-1/run-2since these simulator FlowFiles don't actually carrygrover.framework— that's fine for a quick smoke test, but if you want cleanqiskit/cirqlabels in the report, override this to a literal per-branch value, e.g. by adding anUpdateAttributeon each branch settinggrover.frameworktoqiskit/cirqbefore the comparison) |
Per its docstring, auto-terminate failure — it carries both the
benign "first of pair, waiting" case (compare.status = waiting) and real
parse errors. Wire success → QuantumAssertion.
4. QuantumAssertion
| Property | Value |
|---|---|
| Hellinger Threshold | 0.10 |
| Check Ground Truth | true |
| Reports Directory | reports |
| Flow Name | grover-qiskit-vs-cirq |
It overrides the default relationships entirely — there is no
success/failure here, only pass and fail. Wire both to a
QuanifiReport (or to separate funnels if you'd rather just watch the
queues). Note Check Ground Truth = true requires a test.expected
attribute to actually assert anything; since these FlowFiles don't carry
one, that half of the check is silently skipped and the verdict rests on the
Hellinger-distance check alone.
5. QuanifiReport — Flow Name = grover-assertion. Look for
assert.verdict (PASS/FAIL/DISAGREE), assert.hellinger,
assert.top_result_a/assert.top_result_b in the attributes (_default
card — QuantumAssertion doesn't set report.type). Auto-terminate
success.
6. Start everything and trigger both branches once. Because Qiskit and
Cirq use different bit-order conventions for 11 on a 2-qubit Grover
search, you may see compare.agreement = false on the raw label
comparison even though the underlying physics agrees — that's the
"Gotchas for the (future) vote" bit-order caveat called out in
DATA_DRIVEN_TESTING.md; it's a good opportunity to
see assert.verdict = DISAGREE / FAIL and read the assert.reason text.
Flow 24 — Data-driven test case generation
Components: QuantumTestCaseSource
Why standalone here: wiring its output into the parametrized Grover
branches end-to-end requires three standard NiFi processors
(SplitJson → EvaluateJsonPath → fan-out to ${grover.*}-driven branches) —
that full recipe is already documented as the canvas-wiring diagram in
DATA_DRIVEN_TESTING.md §3. Run it standalone first to
see what it produces, then follow that doc to wire it up for real differential
runs (which would ultimately feed Flow 23's comparison/assertion pair, closing
the loop).
QuantumTestCaseSource| Property | Value | |---|---| | Test Matrix |{"grover.marked_state": ["00", "11", "000"], "grover.num_iterations": ["1", "2"]}(the default — a parameter-axes spec; its Cartesian product yields 6 cases) | | Case ID Prefix |case| | Partition Label | (leave blank) | | Run ID | (leave blank — auto-generates a timestamp-based id per trigger) |
Feed from GenerateFlowFile. Wire success to a funnel; auto-terminate
failure (fires on bad JSON or a zero-case expansion).
2. Start, list the queue: content is a JSON array of 6 row objects, each
stamped with test.case_id (case-000 … case-005), test.run_id
(shared across the batch), and the two axis attributes per row; FlowFile
attributes show testsource.count = 6, testsource.mode = axes.
3. Try the explicit-table form — switch Test Matrix to a JSON array,
e.g.
[{"grover.marked_state": "0000", "test.partition": "all-zeros", "test.expected": "0000"}, {"grover.marked_state": "1", "test.partition": "n=1", "test.expected": "1"}]
— now testsource.mode = table, testsource.count = 2, and each row
carries its own test.partition/test.expected (a per-row ground-truth
oracle that QuantumAssertion in Flow 23 can later check via
Check Ground Truth = true).
Problem encoders (data in, solution out)
These two framework-agnostic processors are the "no Pauli strings" entry
point: raw problem data becomes a Hamiltonian in the shared wire format
(hamiltonian.format = sparse_pauli_op_json), which any *QAOA (or *VQE)
solver consumes. Swapping the solver framework is a one-processor change.
Flow 25 — MaxCut: graph edges → QAOA → report
Components: MaxCutProblem → QiskitQAOA → QiskitAerSimulator →
QuantumQAOAEvaluator → QuanifiReport
What it proves: a user goes from an edge list to an optimal graph
partition without writing a Hamiltonian: the encoder emits the MaxCut Ising
operator whose minimum eigenvalue is exactly -max_cut. QiskitQAOA only
trains the circuit (0.2.0: it no longer samples, has no Shots property,
and emits the trained circuit as qasm2 content); a simulator samples it, and
QuantumQAOAEvaluator scores those samples against the exact reference. See
the QAOA components guide for the full chain and the
0.1.0→0.2.0 migration.
MaxCutProblem| Property | Value | |---|---| | Edges |[[0, 1], [1, 2], [0, 2]](a triangle; default) | | Num Nodes |0(derive from the edges) |
Feed from a GenerateFlowFile trigger. To drive it from data instead,
put the JSON edge list in the FlowFile content: content overrides the
property, which is what makes data-driven runs (Flow 24 style) work.
Wire success → QiskitQAOA; auto-terminate failure.
2. QiskitQAOA — defaults are fine (Layers 2). Wire success →
QiskitAerSimulator; auto-terminate failure.
3. QiskitAerSimulator — Shots 1024, Random Seed 11 (for a
reproducible demo run). Wire success → QuantumQAOAEvaluator;
auto-terminate failure.
4. QuantumQAOAEvaluator — no properties needed; Hamiltonian defaults
to ${hamiltonian.json}, which rides through from QiskitQAOA's output.
Wire success → QuanifiReport; auto-terminate failure.
5. QuanifiReport — Flow Name = maxcut-qaoa. Auto-terminate success.
6. Start all four. In maxcut-qaoa.html the QAOA panel now reflects the
evaluator's attributes: qaoa.exact_minimum = -2.0 (the triangle's max
cut is 2), qaoa.approximation_ratio ≥ 0.99, and a
qaoa.best_measurement that is any 3-bit string except 000/111 (all
balanced partitions of a triangle cut 2 edges).
7. Swap QiskitQAOA for CirqQAOA, QrispQAOA, PennylaneQAOA or
PyquilQAOA: the wire format is byte-identical, so no reconfiguration is
needed upstream. The downstream simulator is swappable too — any counts
engine (Aer, Cirq, Qrisp, PennyLane, Braket, QSharp, pyQuil) works with
any of the five solvers, since QuantumQAOAEvaluator is framework-neutral.
Flow 26 — QUBO matrix → QAOA (framework-swappable)
Components: QuboToHamiltonian → QiskitQAOA → QiskitAerSimulator →
QuantumQAOAEvaluator → QuanifiReport
What it proves: QUBO is the lingua franca of combinatorial optimization
(portfolio selection, scheduling, routing all reduce to it); this one adapter
makes any such problem solvable by every QAOA processor, sampled by any
simulator and scored by one evaluator. The ground bitstring is the binary
solution vector.
QuboToHamiltonian| Property | Value | |---|---| | QUBO Matrix |[[-1, 2], [0, -1]](default) |
For this Q, f(00) = 0, f(01) = f(10) = -1, f(11) = 0: pick exactly
one of the two variables. As with Flow 25, a JSON 2D array in the FlowFile
content overrides the property. Wire success → QiskitQAOA.
2. QiskitQAOA → QiskitAerSimulator (Shots 1024, Random Seed
11) → QuantumQAOAEvaluator → QuanifiReport (Flow Name =
qubo-qaoa), as in Flow 25.
3. Expect qaoa.exact_minimum = -1.0 and qaoa.best_measurement = 01 or
10, both read from QuantumQAOAEvaluator's output. Read the bitstring
left to right: character i is variable i (sim.bit_order = q0_left).
Entanglement & factoring demos
The learner ladder: Bell states (first entanglement), teleportation (first
protocol), Shor (the famous one). All three are all-in-one processors that
need only a GenerateFlowFile trigger.
Flow 27 — Bell state
Components: QiskitBellState → QuanifiReport
What it proves: entanglement in one box: two qubits, two outcomes, perfect
correlation.
QiskitBellState| Property | Value | |---|---| | Bell State |phi_plus| | Shots |1024|
Wire success → QuanifiReport (Flow Name = bell); auto-terminate
failure.
2. Start. The report's counts chart shows only 00 and 11, roughly 50/50,
and bell.correlation = 1.0000: every shot landed on the correlated pair.
3. Try the other three states: psi_plus/psi_minus flip the pair to
01/10; the correlation stays 1.0.
Flow 28 — Quantum teleportation
Components: QiskitTeleportation → QuanifiReport
What it proves: a real dynamic-circuit protocol: mid-circuit measurement
plus classically conditioned corrections, self-verified.
QiskitTeleportation| Property | Value | |---|---| | Theta |1.0471975512(pi/3) | | Phi |0.7853981634(pi/4) | | Shots |1024|
Wire success → QuanifiReport (Flow Name = teleport); auto-terminate
failure.
2. Start. This processor sets no report.type, so the report falls back to
the all-attributes card: look for teleport.fidelity = 1.0000. The counts
keys read c0 c1 c2 left to right; the first two bits (the Bell
measurement) are random across all four values, the third (the
verification readout on the teleported qubit) is always 0: the state
arrived intact regardless of which correction branch fired.
3. Change Theta/Phi to any angles: fidelity stays 1.0. That invariance under
the message state is the protocol working, not a lucky input.
Flow 29 — Shor's algorithm
Components: QrispShor → QuanifiReport
What it proves: the most famous quantum algorithm as a one-property box,
via Qrisp's native shors_alg (the only integrated framework that ships Shor
as a high-level call).
QrispShor| Property | Value | |---|---| | Number To Factor |15|
Wire success → QuanifiReport (Flow Name = shor); wire failure to a
funnel so you can inspect shor.error.
2. Start. The all-attributes card shows shor.factor_1 = 3,
shor.factor_2 = 5 (a couple of seconds on the local simulator). Try
21 (slower). A prime input (e.g. 13) routes to failure with a clear
shor.error: that is the input validation, not a bug.
Amplitude estimation
Flow 30 — Bernoulli amplitude estimation across frameworks
Components: QiskitAmplitudeEstimation → QuanifiReport
What it proves: the estimation counterpart to sampling: all four
frameworks implement the same Bernoulli problem
(A = Ry(2·asin(√p))), so their canonical estimates land on the same grid
sin²(π·y/2^m) and are directly comparable.
QiskitAmplitudeEstimation| Property | Value | |---|---| | Probability |0.2| | Evaluation Qubits |3| | Method |canonical| | Shots |1024|
Wire success → QuanifiReport (Flow Name = qae); auto-terminate
failure.
2. Start. The Amplitude Estimation panel shows ae.estimate = 0.14644661
(the m = 3 grid point nearest 0.2) and ae.mle ≈ 0.2 (the off-grid
maximum-likelihood refinement).
3. Switch Method to iterative: ae.estimate ≈ 0.2 directly, within
Epsilon Target.
4. Swap the processor for CirqAmplitudeEstimation or
PennylaneAmplitudeEstimation with the same Probability/Evaluation
Qubits: the canonical estimate is identical (same grid), which is the
N-version pseudo-oracle for this component. QrispAmplitudeEstimation is
the iterative counterpart (Qrisp's native IQAE): expect ≈ 0.2.
Real hardware execution
Both gateways consume the shared circuit contract, so a circuit built by
any framework's builder (Qiskit, Cirq, Qrisp, PennyLane) runs on real
hardware through them: set the builder's Output Format to qasm2 and
wire it in. Every default below works offline; credentials only matter when
you point at a real device.
Flow 31 — Any circuit → Amazon Braket (local or QPU)
Components: any circuit builder (e.g. QiskitGroverCircuit) →
BraketDevice → QuanifiReport
What it proves: the qasm2 interchange reaching hardware: the same wiring
runs offline today and on an IonQ/Rigetti/IQM QPU when you paste an ARN.
QiskitGroverCircuit— Marked State =11, Num Iterations =1, Output Format =qasm2. Wiresuccess→BraketDevice.BraketDevice| Property | Value | |---|---| | Device |local| | Shots |1024| | Wait For Results |true|
Wire success → QuanifiReport (Flow Name = braket-hw); auto-terminate
failure.
3. Start. Counts concentrate on 11; the attributes carry
hw.provider = aws-braket, hw.device = local.
4. For real hardware: set Device to a Braket ARN (e.g.
arn:aws:braket:::device/quantum-simulator/amazon/sv1, or a QPU ARN),
have AWS credentials in the standard chain (env vars or ~/.aws), and,
for QPUs with long queues, set Wait For Results = false: the
FlowFile continues immediately with hw.task_arn + hw.status instead
of blocking the canvas. Missing credentials route to failure with a
clear hw.error.
5. Cross-framework check: replace the builder with CirqQFTCircuit or
PennylaneFeatureEmbedding (both emit qasm2): a Cirq- or PennyLane-built
circuit on AWS hardware, no translation step on the canvas.
Flow 32 — Any circuit → IBM Quantum (fake backend or real)
Components: any circuit builder → QiskitRuntimeSampler → QuanifiReport
What it proves: the same gateway idea for IBM: one processor, offline
noisy rehearsal and real-QPU submission.
- Reuse the builder from Flow 31 (Output Format =
qasm2), wired intoQiskitRuntimeSampler: | Property | Value | |---|---| | Backend |fake_manila| | Shots |2000|
Wire success → QuanifiReport (Flow Name = ibm-hw); auto-terminate
failure.
2. Start. fake_manila runs locally with the real backend's calibrated
noise model: a Bell/Grover pair now shows a few percent leakage into the
wrong states — that is what the hardware will do to you, rehearsed
offline. Attributes carry hw.mode = local-fake.
3. For real hardware: set Backend = least_busy (or a name like
ibm_brisbane) and put your IBM Quantum token in Token (or use a
saved account). The circuit is transpiled to the backend ISA
automatically. hw.mode flips to cloud.
Flow 34 — Any circuit → IQM Resonance (async submit + REST polling)
(Numbered 34 so the existing flow numbers keep pointing at the same diagrams; it belongs to this hardware section, not to the QML one below.)
Components: any circuit builder → QrispIQMDevice → IQMJobPoller
(self-looping) → QuanifiReport
What it proves: the third gateway, and the one shape the other two don't
show — submission and collection as separate canvas steps. Qrisp's
Backend Interface is asynchronous, so the submit node never blocks on an IQM
queue; a poller drives the job to completion against the IQM Resonance REST
API and re-queues itself while the job is still waiting.
- Reuse the builder from Flow 31 (Output Format =
qasm2— Qrisp's importer is the qasm2 one), wired intoQrispIQMDevice: | Property | Value | |---|---| | Device Instance |local| | Shots |1024| | Wait For Results |true|
Wire success → QuanifiReport (Flow Name = iqm-hw); auto-terminate
failure. Start it: local runs Qrisp's own statevector simulator, so
the whole lane is exercised offline with no token. Attributes carry
hw.provider = iqm-resonance, hw.mode = local-sim.
- Now switch to the asynchronous shape, which is what a real QPU wants.
Set
QrispIQMDevice: | Property | Value | |---|---| | Device Instance |garnet| | API Token |#{iqm.resonance.token}| | Shots |100| | Wait For Results |false|
It now returns immediately with hw.job_id, hw.status and
hw.server_url instead of counts — there is no report.type yet,
because there are no results yet.
IQMJobPollerbetween it and the report: | Property | Value | |---|---| | API Token |#{iqm.resonance.token}| | Job ID |${hw.job_id}| | Poll Timeout Seconds |60| | Poll Interval Seconds |5| | Max Poll Interval Seconds |60|
Set its Run Schedule to 30 sec. Wire success → QuanifiReport,
auto-terminate failure, and — the important one — wire pending back
into IQMJobPoller itself. Each pass polls GET /api/v1/jobs/{id} for
up to 60 s, obeying the server's Retry-After header; a job still in the
queue leaves on pending and comes round again. A three-hour queue costs
a few hundred cheap passes and zero parked threads.
-
When the job reaches
completed, the poller fetchesGET /api/v1/jobs/{id}/artifacts/measurement_counts, normalises to the canonicalq0_leftorder, and emits the usualsim.*+report.type = simulation.QuanifiReportrenders the counts chart plus a Hardware Execution panel (hw.provider,hw.device,hw.job_id,hw.status,hw.poll_attempts) — that panel is vendor-neutral, so Flows 31 and 32 get it too. -
Credentials: create a Parameter Context
IQM Quantum Credentialswith a sensitive parameteriqm.resonance.token, assign it to the process group, and set both API Token properties to exactly#{iqm.resonance.token}(a sensitive property may reference one sensitive Parameter and nothing else). The ready-made group is built bytools/add_iqm_group.py.
QML training
Flow 33 — Dataset → variational classifier → training report
Components: PennylaneDatasetLoader → PennylaneVariationalClassifier →
QuanifiReport
What it proves: the QML lane end to end: machine learning on a quantum
circuit with zero equations, honest train/validation split, and a loss curve
in the report.
PennylaneDatasetLoader| Property | Value | |---|---| | Mode |synthetic| | Num Samples |24| | Num Features |2| | Seed |7|
Feed from GenerateFlowFile. Wire success →
PennylaneVariationalClassifier; auto-terminate failure.
2. PennylaneVariationalClassifier
| Property | Value |
|---|---|
| Embedding | angle |
| Ansatz | strongly_entangling |
| Layers | 2 |
| Epochs | 25 |
| Learning Rate | 0.2 |
The Embedding/Ansatz values use the same vocabulary as
PennylaneFeatureEmbedding and PennylaneVariationalAnsatz (Flow 20), so
the model trained here is the same circuit family those builders emit.
Wire success → QuanifiReport; auto-terminate failure.
3. QuanifiReport — Flow Name = qml-training. Auto-terminate success.
4. Start all three. The card uses the training layout: a loss curve that
visibly decreases, plus train.train_accuracy / train.val_accuracy
(≥ 0.8 on the separable synthetic blobs) and the model summary
(train.embedding, train.ansatz, train.num_qubits).
5. Variations: Embedding = iqp with Ansatz = basic_entangler
trains a different circuit family; Embedding = amplitude packs the
features into ceil(log2(n)) qubits (watch train.num_qubits drop).
Switch the loader's Mode to inline to train on your own records.
Flow 35 — QAOA N×M: 5 builders × 7 engines
Components: QiskitHamiltonian → 5 fixed-angle builders
(QiskitQAOACircuit, CirqQAOACircuit, PennylaneQAOACircuit,
QrispQAOACircuit, PyquilQAOACircuit) → each builder's success fanned
out to all 7 simulators (QiskitAerSimulator, CirqSimulator,
QrispSimulator, PennylaneSimulator, BraketSimulator, QSharpSimulator,
PyquilSimulator) → one shared QuantumQAOAEvaluator → one shared
QuanifiReport.
What it proves: one qasm2 circuit, built by any of the five QAOA
frameworks at the same explicit angles, runs unmodified on any of the
seven counts engines and is scored by one framework-neutral evaluator — the
35-cell matrix this whole extraction exists to enable. It is generated, not
hand-built; see demo/qaoa/qaoa-nxm.json
and the "N×M flow" section of the
QAOA components guide for the full click path,
regeneration command and every measured Hellinger distance.
QiskitHamiltonian— Hamiltonian-0.8 Z0 Z1 + 0.5 Z1 Z2 - 0.3 Z0 + 0.2, Num Qubits3(this isH_NXM; its unique ground state is001, energy-1.4). Wiresuccessto all 5 builders; auto-terminatefailure.- Each builder — Layers
2, Betas[-0.67, -0.42], Gammas[1.14, 1.27](identical across all 5, so every row builds the same angles in its own framework). Wire each builder'ssuccessto all 7 simulators (35 connections total); auto-terminatefailureon each. - Each simulator — the 6 seedable engines use Shots
4096, Random Seed11;BraketSimulatoruses Shots4096only (it has no seed property — see "Known limitations" in the QAOA guide) andPyquilSimulatoruses Shots256(its PyQVM sampling cost is about 13.6 ms/shot; 4096 shots would take tens of seconds per cell). Wire each simulator'ssuccessto the one sharedQuantumQAOAEvaluator; auto-terminatefailure. QuantumQAOAEvaluator— no properties needed. Wiresuccess→QuanifiReport; auto-terminatefailure.QuanifiReport— Flow Name =qaoa-nxm, Reports Directory =reports/qaoa. Auto-terminatesuccess.- Start every processor except the trigger, then Run Once the
QiskitHamiltoniantrigger. All 35 cells reachqaoa.best_measurement = "001"andqaoa.exact_minimum = -1.4. Every seeded 4096-shot cell's Hellinger distance to the exact distribution stays under 0.023 (measured worst case 0.0187, on the seeded engines); the unseededBraketSimulatorcolumn varies run to run but stays within the plan's measured statistical bound (0.9999-quantile 0.030, maximum observed 0.032 across 50000 trials) — never compare its saved value for exact equality.PyquilSimulatorcells (256 shots) stay under 0.044. - This flow is intentionally 5×7 = 35 connections wide; import it as-is
from
demo/qaoa/qaoa-nxm.jsonrather than click-building it by hand.
Quick-reference: which flow exercises which processor
| Processor | Flow(s) |
|---|---|
| QiskitQuantumHelloWorld | 1 |
| QiskitGroverCircuit | 2, 22, 23 |
| QiskitAerSimulator | 2, 3, 4, 5, 6, 23 |
| QiskitCircuitReport | 2 |
| QiskitHadamardTransform | 3 |
| QiskitQFTCircuit | 4 |
| QiskitPhaseEstimation | 5 |
| QiskitAmplitudeAmplification | 6 |
| QiskitStatePreparation | 7 |
| QiskitStatevectorSimulator | 7 |
| QiskitGroverSearch | 8 |
| QiskitHamiltonian | 9 |
| QiskitAnsatz | 9 |
| QiskitVQE | 9 |
| CirqGroverCircuit | 10, 22, 23 |
| CirqSimulator | 10, 11, 12, 13, 14, 23 |
| CirqPhaseOracle | 11 |
| CirqGroverOperator | 11 |
| CirqHadamardTransform | 12 |
| CirqQFTCircuit | 13 |
| CirqPhaseEstimation | 14 |
| CirqHamiltonian | 15 |
| CirqAnsatz | 15 |
| CirqVQE | 15 |
| QrispGroverSearch | 16 |
| QrispQFTCircuit | 17 |
| QrispSimulator | 17 |
| QrispPhaseEstimation | 18 |
| QrispHamiltonian | 19 |
| QrispAnsatz | 19 |
| QrispVQE | 19 |
| PennylaneFeatureEmbedding | 20 |
| PennylaneVariationalAnsatz | 20 |
| PennylaneExpectation | 20 |
| PennylaneDatasetLoader | 21 |
| QuanifiUnitary | 22 |
| QuantumUnitaryComparison | 22 |
| QuantumDistributionComparison | 23 |
| QuantumAssertion | 23 |
| QuantumTestCaseSource | 24 |
| MaxCutProblem | 25 |
| QuboToHamiltonian | 26 |
| QiskitQAOA | 25, 26 (train-only since 0.2.0; sampled by the next hop, QiskitAerSimulator), 35 (as swap-in for the N×M solver-equivalent row context) |
| CirqQAOA / QrispQAOA / PennylaneQAOA / PyquilQAOA | 25, 26 (as swap-ins for the solver) |
| QiskitQAOACircuit / CirqQAOACircuit / PennylaneQAOACircuit / QrispQAOACircuit / PyquilQAOACircuit | 35 |
| QuantumQAOAEvaluator | 25, 26, 35 |
| QiskitBellState | 27 |
| QiskitTeleportation | 28 |
| QrispShor | 29 |
| QiskitAmplitudeEstimation | 30 |
| CirqAmplitudeEstimation / PennylaneAmplitudeEstimation / QrispAmplitudeEstimation | 30 (as swap-ins) |
| BraketDevice | 31 |
| QiskitRuntimeSampler | 32 |
| QrispIQMDevice | 34 |
| IQMJobPoller | 34 |
| PennylaneVariationalClassifier | 33 |
| QuanifiReport | 3, 4, 5, 6, 7 (self-reporting peers aside), 8, 9, 10–15, 17–35 |