@@ -1089,90 +1089,6 @@ impl QdpEngine {
10891089 } ) ?;
10901090 Ok ( PyQuantumLoader :: new ( Some ( iter) ) )
10911091 }
1092- }
1093-
1094- // --- Loader bindings (Linux only; qdp-core pipeline types only built on Linux) ---
1095- #[ cfg( target_os = "linux" ) ]
1096- mod loader_bindings {
1097- use super :: * ;
1098- use pyo3:: exceptions:: PyStopIteration ;
1099- use qdp_core:: { PipelineConfig , PipelineIterator } ;
1100-
1101- /// Rust-backed iterator yielding one QuantumTensor per batch; used by QuantumDataLoader.
1102- #[ pyclass]
1103- pub struct PyQuantumLoader {
1104- inner : Option < PipelineIterator > ,
1105- }
1106-
1107- impl PyQuantumLoader {
1108- pub fn new ( inner : Option < PipelineIterator > ) -> Self {
1109- Self { inner }
1110- }
1111- }
1112-
1113- #[ pymethods]
1114- impl PyQuantumLoader {
1115- fn __iter__ ( slf : PyRef < ' _ , Self > ) -> PyRef < ' _ , Self > {
1116- slf
1117- }
1118-
1119- fn __next__ ( mut slf : PyRefMut < ' _ , Self > ) -> PyResult < QuantumTensor > {
1120- let mut iter: PipelineIterator = match slf. inner . take ( ) {
1121- Some ( i) => i,
1122- None => return Err ( PyStopIteration :: new_err ( "" ) ) ,
1123- } ;
1124- // Call next_batch without releasing GIL (return type *mut DLManagedTensor is !Send).
1125- let result = iter. next_batch ( ) ;
1126- match result {
1127- Ok ( Some ( ptr) ) => {
1128- slf. inner = Some ( iter) ;
1129- Ok ( QuantumTensor {
1130- ptr,
1131- consumed : false ,
1132- } )
1133- }
1134- Ok ( None ) => {
1135- // Exhausted; do not put iterator back
1136- Err ( PyStopIteration :: new_err ( "" ) )
1137- }
1138- Err ( e) => {
1139- slf. inner = Some ( iter) ;
1140- Err ( PyRuntimeError :: new_err ( format ! (
1141- "Pipeline next_batch failed: {}" ,
1142- e
1143- ) ) )
1144- }
1145- }
1146- }
1147- }
1148-
1149- /// Build PipelineConfig from Python args. device_id is 0 (engine does not expose it); iterator uses engine clone with correct device.
1150- pub fn config_from_args (
1151- _engine : & CoreEngine ,
1152- batch_size : usize ,
1153- num_qubits : u32 ,
1154- encoding_method : & str ,
1155- total_batches : usize ,
1156- seed : Option < u64 > ,
1157- ) -> PipelineConfig {
1158- PipelineConfig {
1159- device_id : 0 ,
1160- num_qubits,
1161- batch_size,
1162- total_batches,
1163- encoding_method : encoding_method. to_string ( ) ,
1164- seed,
1165- warmup_batches : 0 ,
1166- }
1167- }
1168-
1169- /// Resolve path from Python str or pathlib.Path (__fspath__).
1170- pub fn path_from_py ( path : & Bound < ' _ , PyAny > ) -> PyResult < String > {
1171- path. extract :: < String > ( ) . or_else ( |_| {
1172- path. call_method0 ( "__fspath__" )
1173- . and_then ( |m| m. extract :: < String > ( ) )
1174- } )
1175- }
11761092
11771093 /// encode directly from a PyTorch CUDA tensor. Internal helper.
11781094 ///
@@ -1312,6 +1228,90 @@ mod loader_bindings {
13121228 }
13131229}
13141230
1231+ // --- Loader bindings (Linux only; qdp-core pipeline types only built on Linux) ---
1232+ #[ cfg( target_os = "linux" ) ]
1233+ mod loader_bindings {
1234+ use super :: * ;
1235+ use pyo3:: exceptions:: PyStopIteration ;
1236+ use qdp_core:: { PipelineConfig , PipelineIterator } ;
1237+
1238+ /// Rust-backed iterator yielding one QuantumTensor per batch; used by QuantumDataLoader.
1239+ #[ pyclass]
1240+ pub struct PyQuantumLoader {
1241+ inner : Option < PipelineIterator > ,
1242+ }
1243+
1244+ impl PyQuantumLoader {
1245+ pub fn new ( inner : Option < PipelineIterator > ) -> Self {
1246+ Self { inner }
1247+ }
1248+ }
1249+
1250+ #[ pymethods]
1251+ impl PyQuantumLoader {
1252+ fn __iter__ ( slf : PyRef < ' _ , Self > ) -> PyRef < ' _ , Self > {
1253+ slf
1254+ }
1255+
1256+ fn __next__ ( mut slf : PyRefMut < ' _ , Self > ) -> PyResult < QuantumTensor > {
1257+ let mut iter: PipelineIterator = match slf. inner . take ( ) {
1258+ Some ( i) => i,
1259+ None => return Err ( PyStopIteration :: new_err ( "" ) ) ,
1260+ } ;
1261+ // Call next_batch without releasing GIL (return type *mut DLManagedTensor is !Send).
1262+ let result = iter. next_batch ( ) ;
1263+ match result {
1264+ Ok ( Some ( ptr) ) => {
1265+ slf. inner = Some ( iter) ;
1266+ Ok ( QuantumTensor {
1267+ ptr,
1268+ consumed : false ,
1269+ } )
1270+ }
1271+ Ok ( None ) => {
1272+ // Exhausted; do not put iterator back
1273+ Err ( PyStopIteration :: new_err ( "" ) )
1274+ }
1275+ Err ( e) => {
1276+ slf. inner = Some ( iter) ;
1277+ Err ( PyRuntimeError :: new_err ( format ! (
1278+ "Pipeline next_batch failed: {}" ,
1279+ e
1280+ ) ) )
1281+ }
1282+ }
1283+ }
1284+ }
1285+
1286+ /// Build PipelineConfig from Python args. device_id is 0 (engine does not expose it); iterator uses engine clone with correct device.
1287+ pub fn config_from_args (
1288+ _engine : & CoreEngine ,
1289+ batch_size : usize ,
1290+ num_qubits : u32 ,
1291+ encoding_method : & str ,
1292+ total_batches : usize ,
1293+ seed : Option < u64 > ,
1294+ ) -> PipelineConfig {
1295+ PipelineConfig {
1296+ device_id : 0 ,
1297+ num_qubits,
1298+ batch_size,
1299+ total_batches,
1300+ encoding_method : encoding_method. to_string ( ) ,
1301+ seed,
1302+ warmup_batches : 0 ,
1303+ }
1304+ }
1305+
1306+ /// Resolve path from Python str or pathlib.Path (__fspath__).
1307+ pub fn path_from_py ( path : & Bound < ' _ , PyAny > ) -> PyResult < String > {
1308+ path. extract :: < String > ( ) . or_else ( |_| {
1309+ path. call_method0 ( "__fspath__" )
1310+ . and_then ( |m| m. extract :: < String > ( ) )
1311+ } )
1312+ }
1313+ }
1314+
13151315#[ cfg( target_os = "linux" ) ]
13161316use loader_bindings:: { PyQuantumLoader , config_from_args, path_from_py} ;
13171317
0 commit comments