Skip to content

Commit c02d0de

Browse files
committed
[WIP] Prototype backend v3 interface
1 parent 9755531 commit c02d0de

12 files changed

Lines changed: 688 additions & 2 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cext/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ qiskit-quantum-info.workspace = true
2020
qiskit-circuit.workspace = true
2121
qiskit-circuit-library.workspace = true
2222
qiskit-transpiler.workspace = true
23+
qiskit-providers.workspace = true
2324
pyo3 = { workspace = true, optional = true }
2425
indexmap.workspace = true
2526
ahash.workspace = true

crates/cext/src/backend.rs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// This code is part of Qiskit.
2+
//
3+
// (C) Copyright IBM 2026
4+
//
5+
// This code is licensed under the Apache License, Version 2.0. You may
6+
// obtain a copy of this license in the LICENSE.txt file in the root directory
7+
// of this source tree or at https://www.apache.org/licenses/LICENSE-2.0.
8+
//
9+
// Any modifications or derivative works of this code must retain this
10+
// copyright notice, and modified files need to carry a notice indicating
11+
// that they have been altered from the originals.
12+
13+
use crate::pointers::{const_ptr_as_ref, mut_ptr_as_ref};
14+
use qiskit_providers::{BackendV3, ExecutionResult, JobV2, QuantumProgram};
15+
use qiskit_transpiler::target::Target;
16+
use std::convert::Infallible;
17+
use std::ffi::{CStr, c_char, c_void};
18+
19+
pub struct CJobInterface {
20+
context: *mut c_void,
21+
job_id: Option<unsafe extern "C" fn(context: *mut c_void) -> *const c_char>,
22+
status: Option<unsafe extern "C" fn(context: *mut c_void) -> *const c_char>,
23+
cancel: Option<unsafe extern "C" fn(context: *mut c_void)>,
24+
result: Option<unsafe extern "C" fn(context: *mut c_void) -> *const ExecutionResult>,
25+
}
26+
27+
pub struct CBackendInterface {
28+
context: *mut c_void,
29+
// init: Option<unsafe extern "C" fn(context: *mut c_void)>,
30+
name: Option<unsafe extern "C" fn(context: *mut c_void) -> *const c_char>,
31+
description: Option<unsafe extern "C" fn(context: *mut c_void) -> *const c_char>,
32+
target: Option<unsafe extern "C" fn(context: *mut c_void) -> *const Target>,
33+
execute_fn: Option<
34+
unsafe extern "C" fn(
35+
context: *mut c_void,
36+
program: *const QuantumProgram,
37+
) -> *mut CJobInterface,
38+
>,
39+
}
40+
41+
#[unsafe(no_mangle)]
42+
pub unsafe extern "C" fn qk_backend_new(context: *mut c_void) -> *mut CBackendInterface {
43+
Box::into_raw(Box::new(CBackendInterface {
44+
context,
45+
name: None,
46+
description: None,
47+
target: None,
48+
execute_fn: None,
49+
}))
50+
}
51+
52+
#[unsafe(no_mangle)]
53+
pub unsafe extern "C" fn qk_backend_set_target(
54+
backend: *mut CBackendInterface,
55+
fn_ptr: unsafe extern "C" fn(context: *mut c_void) -> *const Target,
56+
) {
57+
let backend = unsafe { mut_ptr_as_ref(backend) };
58+
backend.target = Some(fn_ptr);
59+
}
60+
61+
#[unsafe(no_mangle)]
62+
pub unsafe extern "C" fn qk_backend_set_name(
63+
backend: *mut CBackendInterface,
64+
fn_ptr: unsafe extern "C" fn(context: *mut c_void) -> *const c_char,
65+
) {
66+
let backend = unsafe { mut_ptr_as_ref(backend) };
67+
backend.name = Some(fn_ptr);
68+
}
69+
70+
#[unsafe(no_mangle)]
71+
pub unsafe extern "C" fn qk_backend_set_description(
72+
backend: *mut CBackendInterface,
73+
fn_ptr: unsafe extern "C" fn(context: *mut c_void) -> *const c_char,
74+
) {
75+
let backend = unsafe { mut_ptr_as_ref(backend) };
76+
backend.description = Some(fn_ptr);
77+
}
78+
79+
#[unsafe(no_mangle)]
80+
pub unsafe extern "C" fn qk_backend_set_execute(
81+
backend: *mut CBackendInterface,
82+
fn_ptr: unsafe extern "C" fn(
83+
context: *mut c_void,
84+
program: *const QuantumProgram,
85+
) -> *mut CJobInterface,
86+
) {
87+
let backend = unsafe { mut_ptr_as_ref(backend) };
88+
backend.execute_fn = Some(fn_ptr);
89+
}
90+
91+
impl BackendV3 for CBackendInterface {
92+
type Error = Infallible;
93+
94+
fn name(&self) -> Option<&str> {
95+
self.name.and_then(|callback| {
96+
let result = unsafe { callback(self.context) };
97+
if result.is_null() {
98+
None
99+
} else {
100+
Some(unsafe { CStr::from_ptr(result) }.to_str().unwrap())
101+
}
102+
})
103+
}
104+
105+
fn description(&self) -> Option<&str> {
106+
self.description.and_then(|callback| {
107+
let result = unsafe { callback(self.context) };
108+
if result.is_null() {
109+
None
110+
} else {
111+
Some(unsafe { CStr::from_ptr(result) }.to_str().unwrap())
112+
}
113+
})
114+
}
115+
116+
fn target(&mut self) -> Result<&Target, Infallible> {
117+
let Some(callback) = self.target else {
118+
panic!(
119+
"target not set for backend interface, qk_backend_set_target must be set before using the backend"
120+
);
121+
};
122+
let target_ptr = unsafe { callback(self.context) };
123+
124+
Ok(unsafe { const_ptr_as_ref(target_ptr) })
125+
}
126+
127+
fn execute(&self, program: &QuantumProgram) -> impl JobV2<Error = Infallible> {
128+
let Some(callback) = self.execute_fn else {
129+
panic!(
130+
"execute function not set for backend interface. qk_backend_set_execute must be set before using the backend"
131+
);
132+
};
133+
let job_ptr = unsafe { callback(self.context, program) };
134+
unsafe { mut_ptr_as_ref(job_ptr) }
135+
}
136+
}
137+
138+
impl JobV2 for &mut CJobInterface {
139+
type Error = Infallible;
140+
fn job_id(&self) -> &str {
141+
let Some(callback) = self.job_id else {
142+
panic!(
143+
"job id function not set for job interface. qk_job_set_job_id must be set before using this job"
144+
);
145+
};
146+
let result = unsafe { callback(self.context) };
147+
unsafe { CStr::from_ptr(result) }.to_str().unwrap()
148+
}
149+
150+
fn status(&self) -> String {
151+
let Some(callback) = self.status else {
152+
panic!(
153+
"status function not set for job interface. qk_job_set_job_status must be set before using this job"
154+
);
155+
};
156+
let result = unsafe { callback(self.context) };
157+
unsafe { CStr::from_ptr(result) }
158+
.to_str()
159+
.unwrap()
160+
.to_owned()
161+
}
162+
163+
fn cancel(&self) {
164+
let Some(callback) = self.cancel else {
165+
panic!(
166+
"cancel function not set for job interface. qk_job_set_job_cancel must be set before using this job"
167+
);
168+
};
169+
unsafe { callback(self.context) };
170+
}
171+
172+
fn result(&self) -> Result<ExecutionResult, Infallible> {
173+
let Some(callback) = self.result else {
174+
panic!(
175+
"result function not set for job interface. qk_job_set_job_cancel must be set before using this job"
176+
);
177+
};
178+
let result = unsafe { const_ptr_as_ref(callback(self.context)) };
179+
Ok(result.clone())
180+
}
181+
}

crates/cext/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod pointers;
1515
#[cfg(feature = "python_binding")]
1616
mod py;
1717

18+
pub mod backend;
1819
pub mod circuit;
1920
pub mod circuit_library;
2021
pub mod dag;

crates/providers/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ license.workspace = true
99
name = "qiskit_providers"
1010

1111
[dependencies]
12+
qiskit-transpiler.workspace = true
1213

1314
[dependencies.hashbrown]
1415
workspace = true

crates/providers/src/backend_v3.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// This code is part of Qiskit.
2+
//
3+
// (C) Copyright IBM 2026
4+
//
5+
// This code is licensed under the Apache License, Version 2.0. You may
6+
// obtain a copy of this license in the LICENSE.txt file in the root directory
7+
// of this source tree or at https://www.apache.org/licenses/LICENSE-2.0.
8+
//
9+
// Any modifications or derivative works of this code must retain this
10+
// copyright notice, and modified files need to carry a notice indicating
11+
// that they have been altered from the originals.
12+
13+
use qiskit_transpiler::target::Target;
14+
15+
#[derive(Clone)]
16+
pub struct QuantumProgram {
17+
pub placeholder: Vec<u8>,
18+
}
19+
20+
#[derive(Clone)]
21+
pub struct ExecutionResult {
22+
pub placeholder: Vec<u32>,
23+
}
24+
25+
/// This is the trait defining the interface for job objects in the backend interface
26+
pub trait JobV2 {
27+
type Error;
28+
29+
fn job_id(&self) -> &str;
30+
fn result(&self) -> Result<ExecutionResult, Self::Error>;
31+
fn cancel(&self);
32+
fn status(&self) -> String;
33+
}
34+
35+
/// This trait defines the common backend
36+
pub trait BackendV3 {
37+
type Error;
38+
39+
/// An optional name for this backend instance
40+
fn name(&self) -> Option<&str>;
41+
/// An optional description for this backend instance
42+
fn description(&self) -> Option<&str>;
43+
/// The target describing the QPU constraints for executing circuits
44+
fn target(&mut self) -> Result<&Target, Self::Error>;
45+
/// The execution
46+
fn execute(&self, program: &QuantumProgram) -> impl JobV2<Error = Self::Error>;
47+
}

crates/providers/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
// copyright notice, and modified files need to carry a notice indicating
1111
// that they have been altered from the originals.
1212

13+
mod backend_v3;
1314
mod data_tree;
1415

16+
pub use backend_v3::{BackendV3, ExecutionResult, JobV2, QuantumProgram};
1517
pub use data_tree::{DataTree, PathEntry};

crates/pyext/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,5 @@ qiskit-cext-vtable = { workspace = true, features = ["python_binding", "addr"] }
4141
qiskit-transpiler.workspace = true
4242
qiskit-quantum-info.workspace = true
4343
qiskit-synthesis.workspace = true
44+
qiskit-providers.workspace = true
45+
qiskit-util.workspace = true

crates/pyext/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use pyo3::prelude::*;
1414
pub use qiskit_cext::*;
1515

1616
mod capi;
17+
mod providers;
1718

1819
#[inline(always)]
1920
#[doc(hidden)]
@@ -98,7 +99,7 @@ fn _accelerate(m: &Bound<PyModule>) -> PyResult<()> {
9899
add_submodule(m, ::qiskit_transpiler::passes::optimize_clifford_t_mod, "optimize_clifford_t")?;
99100
add_submodule(m, ::qiskit_transpiler::passes::substitute_pi4_rotations_mod, "substitute_pi4_rotations")?;
100101
add_submodule(m, ::qiskit_transpiler::passes::synthesize_rz_rotations_mod, "synthesize_rz_rotations")?;
101-
102102
add_submodule(m, ::qiskit_transpiler::passes::convert_to_pauli_rotations_mod, "convert_to_pauli_rotations")?;
103+
add_submodule(m, providers::providers, "providers")?;
103104
Ok(())
104105
}

0 commit comments

Comments
 (0)