Qiskit SDK v2.6 release summary

Source: IBM•

Qiskit SDK v2.6 release summary

Technical release summary for Qiskit SDK v2.6, including updates on top new features.

  • Quantum Research
  • Blog

Date

22 Oct 2026

Authors

Qiskit Team

Key takeaways:

  • Qiskit SDK v2.6 is a C API–focused release, bringing the compiler's pass-manager framework, QPY serialization, and custom operations to compiled languages.
  • A new C API pass-manager framework lets you write custom compiler passes and lower circuits through multiple type-checked intermediate representations.
  • QPY serialization is now in the C API, enabling programs in other languages including C, C++, and Julia to save and exchange circuits compatible with Python's qiskit.qpy.
  • You can define custom quantum operations, including hardware-native gates, directly from the C API without Python's object model.
  • Qiskit v2.6 makes progress toward enabling use of the C API in environments where Python isn't installed.
  • QPY format v18 is now the default and shrinks serialized circuits; minimum Python stays 3.10, source builds need Rust 1.89.

We're excited to announce the release of Qiskit SDK v2.6! The latest minor release in the v2.x series continues our effort to strengthen the SDK's core infrastructure—making it faster, more extensible, and more capable as a high-performance compiler for quantum computing.

A central story of recent releases has been Qiskit's evolution beyond Python, exposing its high-performance Rust core through a C API that interoperates with nearly every language and opens the door to the tightly-coupled, quantum-centric supercomputing (QCSC) workflows that HPC environments demand. Qiskit v2.6 is, above all, a C API release: it brings the compiler's pass-manager framework, QPY serialization, and custom operations to the C API, and continues the behind-the-scenes work of making it possible to use the C API without having Python installed.

These capabilities are more than conveniences for non-Python users. A compiler that lowers a program through multiple intermediate representations, a serialization format that moves circuits between languages and systems, and an execution model that puts composable control in the user's hands—these are the foundational plumbing for what's next.

That groundwork points toward the QCSC and fault-tolerant workloads to come, even as Qiskit keeps delivering real value for near-term work today. Qiskit is being built to be the performant software stack users reach for across that entire continuum—not only for near-term work, where it already delivers real value, but for the error-corrected pipelines as well.

The TL;DR

Qiskit SDK v2.6 is a release centered on the C API. Previous releases gave C API users the ability to build circuits and run them through Qiskit's transpiler; v2.6 gives them far more control—a customizable, extensible pass-manager framework (including lowering through multiple IRs), QPY serialization, and the ability to define their own operations—all without Python. Highlights include:

  • A new pass-manager framework in the C API. You can now write and execute compiler passes directly from C, and lower a program through multiple intermediate representations (IRs) with type-checked, cross-language pipelines.
  • QPY serialization in the C API. Qiskit's portable binary circuit format can now be written and read from C, operating on native circuits without a Python round-trip, and remaining compatible with Python-produced QPY.
  • Custom operations defined from C. You can now fully define quantum operations directly from the C API and add them to any circuit.
  • Continued progress toward making Python optional. Behind-the-scenes work moves the standalone C library closer to running without Python being installed—a stepping stone toward, for example, letting Julia-on-Windows users run Qiskit.
  • A new QPY format version (v18). 2.6 bumps the QPY format with several improvements (see Upgrade considerations).

Top new features and improvements

New pass-manager framework in the C API

Qiskit v2.6 introduces a generic pass-manager framework to the C API. C API users could already run Qiskit's preset transpilation pipeline; now they can build their own—writing individual compiler passes and composing them into custom pass managers, all from compiled languages.

The new QkPassManager manages execution. It can be populated with QkPass objects via qk_passmanager_push_pass and run with qk_passmanager_run_simple; the pass manager and its passes report errors through QkCompilationError. The functionality of a pass is defined through a vtable (QkPassVtable). Passes declare their input and output IR with a QkIrHandle, which can describe either a dynamically defined C IR or a built-in Qiskit IR (today, QkCircuit and QkDag). The pass manager uses these handles to verify the pipeline—enabling type-checked compiler pipelines.

Crucially, this is the piece that lets a program be lowered through multiple IRs: because each pass declares the IR type it consumes and produces, the pass manager can validate that a multi-stage lowering pipeline is well-formed, and extensions are free to define their own IR types in addition to Qiskit's built-ins.

This continues the multi-representation compiler framework introduced in v2.5, bringing that layered, progressively-lowering compiler model to the C API. It's a foundational step toward a compiler that can take a program from a high-level description down to hardware-specific operations across multiple optimization levels.

The example below defines a custom pass—here, one that removes identity-equivalent gates by calling an existing Qiskit routine—registers it in a vtable, and runs it on a circuit through a QkPassManager:

#include <qiskit.h> /// A struct to keep the configuration of the pass. typedef struct { QkTarget *target; } RemoveIdentity; /// The pass execution (or `run` function). QkCircuit *run_remove_identity(RemoveIdentity *self, QkCircuit *ir, QkPassContext *context, QkCompilationError **error) { // You can implement the custom logic in this function. Here, // we're calling a predefined Qiskit function to remove identity gates. qk_transpiler_pass_standalone_remove_identity_equivalent(ir, self->target, 1.0); return ir; } /// The vtable defining the pass. static const QkVtableEntry remove_identity_slots[] = { {.slot = QkPassSlot_RunOwned, .ptr = run_remove_identity}, {.slot = -1, .ptr = NULL}, }; int main(void) { // A handle to the IR the pass is defined on. This can also be defined // as a custom IR; see `qk_ir_handle_new`. QkIrHandle *ir = qk_ir_handle_builtin(QkIrBuiltin_Circuit); // An instance of a vtable which also defines the name and in/out IRs. QkPassVtable *vtable = qk_pass_vtable_new("remove_identity", ir, ir, remove_identity_slots); // Set up the pass... QkTarget *target = qk_target_new(10); RemoveIdentity this = {target}; QkPass *pass = qk_pass_new((void *)&this, vtable); // ... and add it to the pass manager QkPassManager *pm = qk_passmanager_new(); qk_passmanager_push_pass(pm, pass); // Now we can execute it on a circuit. QkCircuit *circuit = qk_circuit_new(10, 0); uint32_t q0[1] = {0}; double almost_zero[1] = {1e-20}; double nonzero[1] = {1.23}; qk_circuit_gate(circuit, QkGate_H, q0, NULL); qk_circuit_gate(circuit, QkGate_RX, q0, almost_zero); qk_circuit_gate(circuit, QkGate_RZ, q0, nonzero); qk_circuit_gate(circuit, QkGate_H, q0, NULL); // note: The pass manager takes ownership of the input IR, which no longer // needs to be freed -- only the output IR must be freed. QkCircuit *out = (QkCircuit *)qk_passmanager_run_simple(pm, (void *)circuit, ir, ir, NULL); qk_circuit_free(out); qk_target_free(target); qk_passmanager_free(pm); qk_pass_vtable_free(vtable); qk_ir_handle_free(ir); return 0; }

The pass declares the same built-in IR (QkIrBuiltin_Circuit) for both input and output; a lowering pass would instead declare different input and output IR handles, and the pass manager would type-check the transition.

QPY serialization in the C API

QPY—Qiskit's portable, cross-platform binary format for storing quantum circuits—is now accessible from the C API. Multiple QkCircuit objects can be serialized into a single QPY stream, either to an in-memory buffer (qk_qpy_dump_buffer) or to a file (qk_qpy_dump_file), with *_with_version variants to pin the QPY version used. Circuits can be loaded back with qk_qpy_load_file/qk_qpy_load_buffer. Qiskit v2.6 also introduces QPY format v18, now the default, which reduces the size of serialized circuits.

These functions operate on native circuits without interacting with Python, and the data they produce is compatible with the Python qiskit.qpy interface—so a circuit serialized from a C program (or any language calling the C API) can be loaded in Python, and vice versa. (If you're using the C API from inside a Python extension, you should still use the Python qpy.dump/qpy.load functions; the C functions return an error if Python is required to create or load the payload.)

For compiled-language and HPC users, this unlocks two concrete workflows:

  • Persistence across runs. QPY was designed from the start to save circuits to disk and reload them in a later execution, but before Qiskit v2.6, that was only possible from Python. You can now build and compile a circuit in C—or in another compiled language through its C FFI, such as C++, Julia, or Fortran—cache it to disk, and reload it in a subsequent run without ever standing up a Python interpreter.
  • Job submission. QPY is also the format Qiskit uses to submit circuits to IBM Quantum Platform. Exposing it to the C API makes the qiskit-ibm-runtime-c client simpler to maintain because it can lean on Qiskit itself for QPY rather than reimplementing the format. It also makes it far more practical to write your own client layer in another language if you’d rather not depend on qiskit-ibm-runtime-c.

In practice, persisting a compiled circuit from a C program is a single call — and Python picks it up with the QPY interface it already has:

#include <stdio.h> // Assume `qc` is a `QkCircuit *` you've already built. const QkCircuit *circuits[] = { qc }; // Serialize one or more circuits into a single QPY file. // Here, we pass NULL for the error pointer, but you can pass a char** to // write the error message if an error occurs. QkExitCode result = qk_qpy_dump_file(circuits, 1, "circuits.qpy", NULL); if (result != QkExitCode_Success) { // Returns an error if Python would be required to serialize the payload. fprintf(stderr, "QPY dump failed\n"); return 1; }

Later, in a Python analysis step, it loads with no special handling required:

from qiskit import qpy with open("circuits.qpy", "rb") as f: circuits = qpy.load(f) # standard qiskit.qpy, same as any .qpy file

Defining custom operations from C

Qiskit v2.6 lets you define your own operations—including custom gates—directly from the C API. Custom operations follow the same extensibility pattern used throughout Qiskit's C API. Just like the custom passes and custom IRs in the new pass-manager framework, a QkCustomOp's functionality is defined by a vtable built from QkVtableEntry slots. Once defined, you add your operation to a circuit with qk_circuit_custom_operation, or to a DAG with qk_dag_apply_custom_operation.

The vtable is the interface Qiskit's data model uses to work with your operation. It declares the set of functions Qiskit expects an operation to provide, which you implement in C. You provide a set of slots that define how your operation behaves—covering, among other things, its name, its number of qubits, its definition, whether it is unitary, and its equality, clone, and delete behavior.

Only two are required: the name and qubit count, supplied through the QkCustomOpSlot_Name and QkCustomOpSlot_NumQubits slots. The rest—including optional slots for a label or for marking the operation as a directive—fall back to sensible defaults when omitted. The circuit then works with your operation through dynamic dispatch, so it never needs to know your operation's concrete type, yet can use it exactly like any built-in operation.

For the complete, authoritative list of slots, see the QkCustomOp documentation in our C API docs.

This extensibility model makes it possible to introduce high-performance, hardware-native custom gates into Qiskit circuits without the Python object model. For example, here's a custom gate named foo that carries its own qubit count, with its action defined as a layer of Hadamards:

#include <stdlib.h> #include <string.h> #include <qiskit.h> typedef struct { uint32_t num_qubits; } FooGate; static const char *foo_name(const FooGate *gate) { (void)gate; // Unused. return "foo"; } static uint32_t foo_num_qubits(const FooGate *gate) { return gate->num_qubits; } static bool foo_eq(const FooGate *gate, const FooGate *other) { return (gate->num_qubits == other->num_qubits); } static FooGate *foo_clone(const FooGate *gate) { FooGate *out = malloc(sizeof(*out)); memcpy(out, gate, sizeof(*out)); return out; } /// The definition of the gate in terms of a `QkCircuit*`. Here the /// gate is defined by a simple layer of Hadamard gates. QkCircuit *foo_definition(const FooGate *gate, QkParam **params) { (void)params; // Unused. QkCircuit *def = qk_circuit_new(gate->num_qubits, 0); for (uint32_t i = 0; i < gate->num_qubits; i++) { qk_circuit_gate(def, QkGate_H, (uint32_t[1]){i}, NULL); } return def; } /// The vtable entries -- note this is the same infrastructure as for the custom passes and IRs. static const QkVtableEntry foo_entries[] = { {.slot = QkCustomOpSlot_Name, .ptr = foo_name}, {.slot = QkCustomOpSlot_NumQubits, .ptr = foo_num_qubits}, {.slot = QkCustomOpSlot_Eq, .ptr = foo_eq}, {.slot = QkCustomOpSlot_Definition, .ptr = foo_definition}, {.slot = QkCustomOpSlot_Clone, .ptr = foo_clone}, {.slot = QkCustomOpSlot_Delete, .ptr = free}, {.slot = -1, .ptr = NULL}, }; int main(void) { // The QkCustomOp takes ownership so we need to explicitly allocate the object. FooGate *foo_config = malloc(sizeof(FooGate)); foo_config->num_qubits = 3; const QkCustomOpVtable *foo_vtable = qk_custom_operation_vtable_new(foo_entries); QkCustomOp *foo = qk_custom_operation_new(foo_config, foo_vtable); QkCircuit *circuit = qk_circuit_new(3, 0); uint32_t qubits[3] = {0, 1, 2}; qk_circuit_custom_operation(circuit, foo, qubits, NULL, NULL); qk_circuit_free(circuit); qk_custom_operation_vtable_free(foo_vtable); return 0; }

Toward a Python-optional Qiskit

The C API supports two modes of operation: a standalone shared library for working with Qiskit from a compiled language with no runtime dependency on the Python interpreter, and a Python-extension mode for building Python extensions that talk directly to Qiskit's Rust objects for better performance.

One longstanding limitation of standalone mode is that, even though it never starts the Python interpreter, it has still required Python to be installed. That's because Qiskit's internal Rust data model is defined partly in terms of Python types, and the linker needs those symbols present even though the C API never uses them (issue #14240). Qiskit v2.6 makes good progress toward lifting this restriction, and we hope to finish the job in v2.7. This work will open up the C API to environments where installing Python previously wasn't practical.

Upgrade considerations

For most users, we expect that upgrading to Qiskit v2.6 should not require code changes. The points below may be relevant depending on how you use the SDK—particularly if you work with QPY serialization, build Qiskit from source, or use the C API.

QPY format version

Qiskit v2.6 introduces a new QPY format version, v18, which qiskit.qpy.dump() now emits by default. The practical impact is compatibility in one direction: QPY written by v2.6 may not load in older Qiskit releases.

If you need to generate a QPY file that an older release of Qiskit can load, you can request an older format version. Use the version argument of qiskit.qpy.dump() in Python, or the qk_qpy_dump_*_with_version() functions in the C API, to specify a QPY version older than 18. Check the format version history to see which versions are supported by which Qiskit releases.

The v18 changes are largely internal cleanup, along with a more compact on-disk encoding of registers. The difference is negligible for most circuits, but circuits with very large registers or many classical registers can see a meaningful reduction in file size.

Minimum supported Python version

The minimum supported Python version remains 3.10 (requires-python = ">=3.10" on the 2.6 branch). This is the last Qiskit version that will support 3.10. Starting in Qiskit 2.7, Python 3.11 or newer will be required.

Build / dependency requirements

Building Qiskit from source now requires Rust 1.89 (MSRV bump on the branch). This does not affect users installing pre-built wheels via pip. There are no glibc compatibility changes in this release.

C API stability / binary compatibility

With Qiskit v2.6, we are not yet declaring the C API stable. However, it’s worth noting that there were no breaking changes to the C API between v2.5 and v2.6. Changes in this release are additive, so existing C code should continue to compile and link against 2.6 without modification.

Final notes

And there you have it! The most important updates in Qiskit v2.6.

If you'd like to help shape where Qiskit goes next, you can always open a GitHub issue to request a feature or report a bug, and you can follow along with our plans on the Qiskit roadmap.

Special thanks to many developers that contributed to this release (in alphabetical order):

Abby Mitchell, Aditya Srivastava, Alexander Ivrii, Aly Abouzaid, Andrew Eddins, Anita George, Arpit Jha, Aziz Chebil, Bryan Johnnson, Daksh Singh, Daniel Gaskins, David F. Cohen, Dennis Wayo, Dikran Meliksetian, Doraking, dp1345, Elad Venezian, Eli Arbel, Ella Barkan, Esteban Ramírez, Evan McKinney, Gadi Aleksandrowicz, Greg Neighbors, HaEumKim, Ian Hincks, Jake Lishman, Jessie Yu, Jim Garrison, José Miguel Alvarez Marques De Miranda, Joshua Skanes-Norman, Julien Gacon, Kazuki Tsuoka, Kevin Hartman, kmbys, Luciano Bello, Matteo AC Rossi, Matthew Treinish, Max Rossmannek, Mehar Ali, metamyte, Miriyala Jeevan Kumar, Mohamed Mahameed, Mozammil Hassan, Patrick Hopf, Pedro Henrique de Oliveira, Peter Chen J., philipjpark, Pramod Anandarao, Raynel Sanchez, Rebecca Dimock, Ross Peili, Sam Odler, Shelly Garion, shreyasavadatti, Simona Rabinovici-Cohen, Sivan Ravid, Sona Jomon, Tal Kozlovski, Tilock Sadhukhan, Vempati Harsha, Vyom Patel, Yevgeny Yaroker, Yosef Bunick, zeb33n

Keep an eye on the IBM Quantum blog for more on the C API, the language bindings, and what's coming next for the Qiskit compiler.

What this article says