use crate::error::{MLError, Result};
use quantrs2_circuit::prelude::*;
use quantrs2_core::prelude::*;
use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
use scirs2_core::Complex64;
use quantrs2_sim::prelude::{MPSSimulator, PauliString, StateVectorSimulator};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub enum DynamicCircuit {
Circuit1(Circuit<1>),
Circuit2(Circuit<2>),
Circuit4(Circuit<4>),
Circuit8(Circuit<8>),
Circuit16(Circuit<16>),
Circuit32(Circuit<32>),
Circuit64(Circuit<64>),
}
impl DynamicCircuit {
pub fn from_circuit<const N: usize>(circuit: Circuit<N>) -> Result<Self> {
match N {
1 => Ok(DynamicCircuit::Circuit1(unsafe {
std::mem::transmute(circuit)
})),
2 => Ok(DynamicCircuit::Circuit2(unsafe {
std::mem::transmute(circuit)
})),
4 => Ok(DynamicCircuit::Circuit4(unsafe {
std::mem::transmute(circuit)
})),
8 => Ok(DynamicCircuit::Circuit8(unsafe {
std::mem::transmute(circuit)
})),
16 => Ok(DynamicCircuit::Circuit16(unsafe {
std::mem::transmute(circuit)
})),
32 => Ok(DynamicCircuit::Circuit32(unsafe {
std::mem::transmute(circuit)
})),
64 => Ok(DynamicCircuit::Circuit64(unsafe {
std::mem::transmute(circuit)
})),
_ => Err(MLError::ValidationError(format!(
"Unsupported circuit size: {}",
N
))),
}
}
pub fn num_qubits(&self) -> usize {
match self {
DynamicCircuit::Circuit1(_) => 1,
DynamicCircuit::Circuit2(_) => 2,
DynamicCircuit::Circuit4(_) => 4,
DynamicCircuit::Circuit8(_) => 8,
DynamicCircuit::Circuit16(_) => 16,
DynamicCircuit::Circuit32(_) => 32,
DynamicCircuit::Circuit64(_) => 64,
}
}
pub fn num_gates(&self) -> usize {
match self {
DynamicCircuit::Circuit1(c) => c.gates().len(),
DynamicCircuit::Circuit2(c) => c.gates().len(),
DynamicCircuit::Circuit4(c) => c.gates().len(),
DynamicCircuit::Circuit8(c) => c.gates().len(),
DynamicCircuit::Circuit16(c) => c.gates().len(),
DynamicCircuit::Circuit32(c) => c.gates().len(),
DynamicCircuit::Circuit64(c) => c.gates().len(),
}
}
pub fn depth(&self) -> usize {
self.num_gates()
}
pub fn gates(&self) -> Vec<&dyn quantrs2_core::gate::GateOp> {
match self {
DynamicCircuit::Circuit1(c) => c
.gates()
.iter()
.map(|g| g.as_ref() as &dyn quantrs2_core::gate::GateOp)
.collect(),
DynamicCircuit::Circuit2(c) => c
.gates()
.iter()
.map(|g| g.as_ref() as &dyn quantrs2_core::gate::GateOp)
.collect(),
DynamicCircuit::Circuit4(c) => c
.gates()
.iter()
.map(|g| g.as_ref() as &dyn quantrs2_core::gate::GateOp)
.collect(),
DynamicCircuit::Circuit8(c) => c
.gates()
.iter()
.map(|g| g.as_ref() as &dyn quantrs2_core::gate::GateOp)
.collect(),
DynamicCircuit::Circuit16(c) => c
.gates()
.iter()
.map(|g| g.as_ref() as &dyn quantrs2_core::gate::GateOp)
.collect(),
DynamicCircuit::Circuit32(c) => c
.gates()
.iter()
.map(|g| g.as_ref() as &dyn quantrs2_core::gate::GateOp)
.collect(),
DynamicCircuit::Circuit64(c) => c
.gates()
.iter()
.map(|g| g.as_ref() as &dyn quantrs2_core::gate::GateOp)
.collect(),
}
}
}
pub trait SimulatorBackend: Send + Sync {
fn execute_circuit(
&self,
circuit: &DynamicCircuit,
parameters: &[f64],
shots: Option<usize>,
) -> Result<SimulationResult>;
fn expectation_value(
&self,
circuit: &DynamicCircuit,
parameters: &[f64],
observable: &Observable,
) -> Result<f64>;
fn compute_gradients(
&self,
circuit: &DynamicCircuit,
parameters: &[f64],
observable: &Observable,
gradient_method: GradientMethod,
) -> Result<Array1<f64>>;
fn capabilities(&self) -> BackendCapabilities;
fn name(&self) -> &str;
fn max_qubits(&self) -> usize;
fn supports_noise(&self) -> bool;
}
#[derive(Debug, Clone)]
pub struct SimulationResult {
pub state: Option<Array1<Complex64>>,
pub measurements: Option<Array1<usize>>,
pub probabilities: Option<Array1<f64>>,
pub metadata: HashMap<String, f64>,
}
#[derive(Debug, Clone)]
pub enum Observable {
PauliString(PauliString),
PauliZ(Vec<usize>),
Matrix(Array2<Complex64>),
Hamiltonian(Vec<(f64, PauliString)>),
}
#[derive(Debug, Clone, Copy)]
pub enum GradientMethod {
ParameterShift,
FiniteDifference,
Adjoint,
StochasticParameterShift,
}
#[derive(Debug, Clone, Default)]
pub struct BackendCapabilities {
pub max_qubits: usize,
pub noise_simulation: bool,
pub gpu_acceleration: bool,
pub distributed: bool,
pub adjoint_gradients: bool,
pub memory_per_qubit: usize,
}
#[derive(Debug)]
pub struct StatevectorBackend {
simulator: StateVectorSimulator,
max_qubits: usize,
}
impl StatevectorBackend {
pub fn new(max_qubits: usize) -> Self {
Self {
simulator: StateVectorSimulator::new(),
max_qubits,
}
}
}
impl SimulatorBackend for StatevectorBackend {
fn execute_circuit(
&self,
circuit: &DynamicCircuit,
_parameters: &[f64],
_shots: Option<usize>,
) -> Result<SimulationResult> {
fn register_to_array(amplitudes: &[Complex64]) -> Array1<Complex64> {
Array1::from_vec(amplitudes.to_vec())
}
macro_rules! run_circuit {
($c:expr) => {{
let state = self.simulator.run($c)?;
let amps = register_to_array(state.amplitudes());
let probabilities: Vec<f64> = amps.iter().map(|c| c.norm_sqr()).collect();
Ok(SimulationResult {
state: Some(amps),
measurements: None,
probabilities: Some(Array1::from_vec(probabilities)),
metadata: HashMap::new(),
})
}};
}
match circuit {
DynamicCircuit::Circuit1(c) => run_circuit!(c),
DynamicCircuit::Circuit2(c) => run_circuit!(c),
DynamicCircuit::Circuit4(c) => run_circuit!(c),
DynamicCircuit::Circuit8(c) => run_circuit!(c),
DynamicCircuit::Circuit16(c) => run_circuit!(c),
DynamicCircuit::Circuit32(c) => run_circuit!(c),
DynamicCircuit::Circuit64(c) => run_circuit!(c),
}
}
fn expectation_value(
&self,
circuit: &DynamicCircuit,
_parameters: &[f64],
observable: &Observable,
) -> Result<f64> {
macro_rules! run_and_expect {
($c:expr) => {{
let state = self.simulator.run($c)?;
let amps = Array1::from_vec(state.amplitudes().to_vec());
self.compute_expectation(&s, observable)
}};
}
match circuit {
DynamicCircuit::Circuit1(c) => run_and_expect!(c),
DynamicCircuit::Circuit2(c) => run_and_expect!(c),
DynamicCircuit::Circuit4(c) => run_and_expect!(c),
DynamicCircuit::Circuit8(c) => run_and_expect!(c),
DynamicCircuit::Circuit16(c) => run_and_expect!(c),
DynamicCircuit::Circuit32(c) => run_and_expect!(c),
DynamicCircuit::Circuit64(c) => run_and_expect!(c),
}
}
fn compute_gradients(
&self,
circuit: &DynamicCircuit,
_parameters: &[f64],
_observable: &Observable,
_gradient_method: GradientMethod,
) -> Result<Array1<f64>> {
match circuit {
DynamicCircuit::Circuit1(_) => Ok(Array1::zeros(1)),
DynamicCircuit::Circuit2(_) => Ok(Array1::zeros(1)),
_ => Err(MLError::ValidationError(
"Unsupported circuit size".to_string(),
)),
}
}
fn capabilities(&self) -> BackendCapabilities {
BackendCapabilities {
max_qubits: self.max_qubits,
noise_simulation: false,
gpu_acceleration: false,
distributed: false,
adjoint_gradients: false,
memory_per_qubit: 16, }
}
fn name(&self) -> &str {
"statevector"
}
fn max_qubits(&self) -> usize {
self.max_qubits
}
fn supports_noise(&self) -> bool {
false
}
}
impl StatevectorBackend {
fn pauli_expectation_single(
&self,
state: &Array1<Complex64>,
pauli: char,
qubit_idx: usize,
) -> Result<f64> {
let dim = state.len();
if dim == 0 {
return Err(MLError::ValidationError("Empty statevector".to_string()));
}
if dim & (dim - 1) != 0 {
return Err(MLError::ValidationError(format!(
"Statevector dimension {dim} is not a power of 2"
)));
}
let n = dim.trailing_zeros() as usize; if qubit_idx >= n {
return Err(MLError::ValidationError(format!(
"Qubit index {qubit_idx} out of range for {n}-qubit state"
)));
}
let bit = 1usize << qubit_idx;
match pauli {
'I' => Ok(1.0),
'Z' => {
let mut expectation = 0.0_f64;
for (j, amp) in state.iter().enumerate() {
let prob = amp.norm_sqr();
if j & bit == 0 {
expectation += prob; } else {
expectation -= prob; }
}
Ok(expectation)
}
'X' => {
let mut sum = Complex64::new(0.0, 0.0);
for (j, amp) in state.iter().enumerate() {
if j & bit == 0 {
let partner = j ^ bit;
if partner < dim {
sum += amp.conj() * state[partner];
}
}
}
Ok(2.0 * sum.re)
}
'Y' => {
let mut sum = Complex64::new(0.0, 0.0);
for (j, amp) in state.iter().enumerate() {
if j & bit == 0 {
let partner = j ^ bit;
if partner < dim {
sum += amp.conj() * state[partner];
}
}
}
Ok(2.0 * sum.im)
}
_ => Err(MLError::ValidationError(format!(
"Unknown Pauli operator '{pauli}'"
))),
}
}
fn compute_expectation(
&self,
state: &Array1<Complex64>,
observable: &Observable,
) -> Result<f64> {
match observable {
Observable::PauliString(pauli_string) => {
use quantrs2_sim::prelude::PauliOperator;
let dim = state.len();
let mut result_vec = state.clone();
for (qubit_idx, pauli_op) in pauli_string.operators.iter().enumerate() {
let bit = 1usize << qubit_idx;
match pauli_op {
PauliOperator::I => {} PauliOperator::Z => {
for j in 0..dim {
if j & bit != 0 {
result_vec[j] = -result_vec[j];
}
}
}
PauliOperator::X => {
for j in 0..dim {
if j & bit == 0 {
let partner = j ^ bit;
if partner < dim {
result_vec.swap(j, partner);
}
}
}
}
PauliOperator::Y => {
let mut new_vec = result_vec.clone();
for j in 0..dim {
if j & bit == 0 {
let partner = j ^ bit;
if partner < dim {
let orig_0 = result_vec[j];
let orig_1 = result_vec[partner];
new_vec[j] = Complex64::new(0.0, -1.0) * orig_1;
new_vec[partner] = Complex64::new(0.0, 1.0) * orig_0;
}
}
}
result_vec = new_vec;
}
}
}
let inner: Complex64 = state
.iter()
.zip(result_vec.iter())
.map(|(&a, &b)| a.conj() * b)
.sum();
Ok((pauli_string.coefficient * inner).re)
}
Observable::PauliZ(qubits) => {
let dim = state.len();
let mut expectation = 0.0_f64;
for (j, amp) in state.iter().enumerate() {
let parity: u32 = qubits
.iter()
.map(|&q| if j & (1 << q) != 0 { 1u32 } else { 0u32 })
.sum::<u32>()
% 2;
let eigenvalue = if parity == 0 { 1.0 } else { -1.0 };
expectation += eigenvalue * amp.norm_sqr();
}
Ok(expectation)
}
Observable::Matrix(matrix) => {
let result: Complex64 = state
.iter()
.enumerate()
.map(|(i, &_i)| {
state
.iter()
.enumerate()
.map(|(j, &_j)| amp_i.conj() * matrix[[i, j]] * amp_j)
.sum::<Complex64>()
})
.sum();
Ok(result.re)
}
Observable::Hamiltonian(terms) => {
let mut expectation = 0.0_f64;
for (coeff, pauli_string) in terms {
let term_exp = self.compute_expectation(
state,
&Observable::PauliString(pauli_string.clone()),
)?;
expectation += coeff * term_exp;
}
Ok(expectation)
}
}
}
fn max_qubits(&self) -> usize {
self.max_qubits
}
fn supports_noise(&self) -> bool {
false
}
}
pub struct MPSBackend {
simulator: MPSSimulator,
bond_dimension: usize,
max_qubits: usize,
}
impl MPSBackend {
pub fn new(bond_dimension: usize, max_qubits: usize) -> Self {
Self {
simulator: MPSSimulator::new(bond_dimension),
bond_dimension,
max_qubits,
}
}
}
impl SimulatorBackend for MPSBackend {
fn execute_circuit(
&self,
circuit: &DynamicCircuit,
_parameters: &[f64],
_shots: Option<usize>,
) -> Result<SimulationResult> {
match circuit {
DynamicCircuit::Circuit1(c) => {
Ok(SimulationResult {
state: None, measurements: None,
probabilities: None,
metadata: {
let mut meta = HashMap::new();
meta.insert("bond_dimension".to_string(), self.bond_dimension as f64);
meta.insert("num_qubits".to_string(), 1.0);
meta
},
})
}
DynamicCircuit::Circuit2(c) => Ok(SimulationResult {
state: None,
measurements: None,
probabilities: None,
metadata: {
let mut meta = HashMap::new();
meta.insert("bond_dimension".to_string(), self.bond_dimension as f64);
meta.insert("num_qubits".to_string(), 2.0);
meta
},
}),
_ => {
Ok(SimulationResult {
state: None,
measurements: None,
probabilities: None,
metadata: {
let mut meta = HashMap::new();
meta.insert("bond_dimension".to_string(), self.bond_dimension as f64);
meta.insert("num_qubits".to_string(), circuit.num_qubits() as f64);
meta
},
})
}
}
}
fn expectation_value(
&self,
circuit: &DynamicCircuit,
_parameters: &[f64],
observable: &Observable,
) -> Result<f64> {
match observable {
Observable::PauliString(_pauli) => {
Ok(0.0) }
Observable::PauliZ(_qubits) => {
Ok(0.0) }
Observable::Hamiltonian(terms) => {
let mut expectation = 0.0;
for (coeff, _pauli) in terms {
expectation += coeff * 0.0; }
Ok(expectation)
}
Observable::Matrix(_) => Err(MLError::NotSupported(
"Matrix observables not supported for MPS backend".to_string(),
)),
}
}
fn compute_gradients(
&self,
circuit: &DynamicCircuit,
parameters: &[f64],
observable: &Observable,
gradient_method: GradientMethod,
) -> Result<Array1<f64>> {
match gradient_method {
GradientMethod::ParameterShift => {
self.parameter_shift_gradients_dynamic(circuit, parameters, observable)
}
_ => Err(MLError::NotSupported(
"Only parameter shift gradients supported for MPS backend".to_string(),
)),
}
}
fn capabilities(&self) -> BackendCapabilities {
BackendCapabilities {
max_qubits: self.max_qubits,
noise_simulation: false,
gpu_acceleration: false,
distributed: false,
adjoint_gradients: false,
memory_per_qubit: self.bond_dimension * self.bond_dimension * 16, }
}
fn name(&self) -> &str {
"mps"
}
fn max_qubits(&self) -> usize {
self.max_qubits
}
fn supports_noise(&self) -> bool {
false
}
}
impl MPSBackend {
fn parameter_shift_gradients_dynamic(
&self,
circuit: &DynamicCircuit,
parameters: &[f64],
observable: &Observable,
) -> Result<Array1<f64>> {
let shift = std::f64::consts::PI / 2.0;
let mut gradients = Array1::zeros(parameters.len());
for i in 0..parameters.len() {
let mut params_plus = parameters.to_vec();
params_plus[i] += shift;
let val_plus = self.expectation_value(circuit, ¶ms_plus, observable)?;
let mut params_minus = parameters.to_vec();
params_minus[i] -= shift;
let val_minus = self.expectation_value(circuit, ¶ms_minus, observable)?;
gradients[i] = (val_plus - val_minus) / 2.0;
}
Ok(gradients)
}
}
#[cfg(feature = "gpu")]
pub use crate::gpu_backend_impl::GPUBackend;
pub enum Backend {
Statevector(StatevectorBackend),
MPS(MPSBackend),
#[cfg(feature = "gpu")]
GPU(GPUBackend),
}
impl SimulatorBackend for Backend {
fn execute_circuit(
&self,
circuit: &DynamicCircuit,
parameters: &[f64],
shots: Option<usize>,
) -> Result<SimulationResult> {
match self {
Backend::Statevector(backend) => backend.execute_circuit(circuit, parameters, shots),
Backend::MPS(backend) => backend.execute_circuit(circuit, parameters, shots),
#[cfg(feature = "gpu")]
Backend::GPU(backend) => backend.execute_circuit(circuit, parameters, shots),
}
}
fn expectation_value(
&self,
circuit: &DynamicCircuit,
parameters: &[f64],
observable: &Observable,
) -> Result<f64> {
match self {
Backend::Statevector(backend) => {
backend.expectation_value(circuit, parameters, observable)
}
Backend::MPS(backend) => backend.expectation_value(circuit, parameters, observable),
#[cfg(feature = "gpu")]
Backend::GPU(backend) => backend.expectation_value(circuit, parameters, observable),
}
}
fn compute_gradients(
&self,
circuit: &DynamicCircuit,
parameters: &[f64],
observable: &Observable,
gradient_method: GradientMethod,
) -> Result<Array1<f64>> {
match self {
Backend::Statevector(backend) => {
backend.compute_gradients(circuit, parameters, observable, gradient_method)
}
Backend::MPS(backend) => {
backend.compute_gradients(circuit, parameters, observable, gradient_method)
}
#[cfg(feature = "gpu")]
Backend::GPU(backend) => {
backend.compute_gradients(circuit, parameters, observable, gradient_method)
}
}
}
fn capabilities(&self) -> BackendCapabilities {
match self {
Backend::Statevector(backend) => backend.capabilities(),
Backend::MPS(backend) => backend.capabilities(),
#[cfg(feature = "gpu")]
Backend::GPU(backend) => backend.capabilities(),
}
}
fn name(&self) -> &str {
match self {
Backend::Statevector(backend) => backend.name(),
Backend::MPS(backend) => backend.name(),
#[cfg(feature = "gpu")]
Backend::GPU(backend) => backend.name(),
}
}
fn max_qubits(&self) -> usize {
match self {
Backend::Statevector(backend) => backend.max_qubits(),
Backend::MPS(backend) => backend.max_qubits(),
#[cfg(feature = "gpu")]
Backend::GPU(backend) => backend.max_qubits(),
}
}
fn supports_noise(&self) -> bool {
match self {
Backend::Statevector(backend) => backend.supports_noise(),
Backend::MPS(backend) => backend.supports_noise(),
#[cfg(feature = "gpu")]
Backend::GPU(backend) => backend.supports_noise(),
}
}
}
pub struct BackendManager {
backends: HashMap<String, Backend>,
current_backend: Option<String>,
selection_strategy: BackendSelectionStrategy,
}
#[derive(Debug, Clone)]
pub enum BackendSelectionStrategy {
Fastest,
MemoryEfficient,
MostAccurate,
Manual(String),
}
impl BackendManager {
pub fn new() -> Self {
Self {
backends: HashMap::new(),
current_backend: None,
selection_strategy: BackendSelectionStrategy::Fastest,
}
}
pub fn register_backend(&mut self, name: impl Into<String>, backend: Backend) {
self.backends.insert(name.into(), backend);
}
pub fn set_strategy(&mut self, strategy: BackendSelectionStrategy) {
self.selection_strategy = strategy;
}
pub fn select_backend(&mut self, num_qubits: usize, shots: Option<usize>) -> Result<()> {
let backend_name = match &self.selection_strategy {
BackendSelectionStrategy::Fastest => self.select_fastest_backend(num_qubits, shots)?,
BackendSelectionStrategy::MemoryEfficient => {
self.select_memory_efficient_backend(num_qubits)?
}
BackendSelectionStrategy::MostAccurate => {
self.select_most_accurate_backend(num_qubits)?
}
BackendSelectionStrategy::Manual(name) => name.clone(),
};
self.current_backend = Some(backend_name);
Ok(())
}
pub fn execute_circuit<const N: usize>(
&self,
circuit: &Circuit<N>,
parameters: &[f64],
shots: Option<usize>,
) -> Result<SimulationResult> {
if let Some(ref backend_name) = self.current_backend {
if let Some(backend) = self.backends.get(backend_name) {
let dynamic_circuit = DynamicCircuit::from_circuit(circuit.clone())?;
backend.execute_circuit(&dynamic_circuit, parameters, shots)
} else {
Err(MLError::InvalidConfiguration(format!(
"Backend '{}' not found",
backend_name
)))
}
} else {
Err(MLError::InvalidConfiguration(
"No backend selected".to_string(),
))
}
}
pub fn current_backend(&self) -> Option<&Backend> {
self.current_backend
.as_ref()
.and_then(|name| self.backends.get(name))
}
pub fn list_backends(&self) -> Vec<(String, BackendCapabilities)> {
self.backends
.iter()
.map(|(name, backend)| (name.clone(), backend.capabilities()))
.collect()
}
fn select_fastest_backend(&self, num_qubits: usize, _shots: Option<usize>) -> Result<String> {
if num_qubits <= 20 {
Ok("statevector".to_string())
} else if num_qubits <= 50 && self.backends.contains_key("gpu") {
Ok("gpu".to_string())
} else if self.backends.contains_key("mps") {
Ok("mps".to_string())
} else {
Err(MLError::InvalidConfiguration(
"No suitable backend for problem size".to_string(),
))
}
}
fn select_memory_efficient_backend(&self, num_qubits: usize) -> Result<String> {
if num_qubits > 30 && self.backends.contains_key("mps") {
Ok("mps".to_string())
} else {
Ok("statevector".to_string())
}
}
fn select_most_accurate_backend(&self, _num_qubits: usize) -> Result<String> {
Ok("statevector".to_string())
}
}
pub mod backend_utils {
use super::*;
pub fn create_default_manager() -> BackendManager {
let mut manager = BackendManager::new();
manager.register_backend(
"statevector",
Backend::Statevector(StatevectorBackend::new(25)),
);
manager.register_backend("mps", Backend::MPS(MPSBackend::new(64, 100)));
#[cfg(feature = "gpu")]
{
if let Ok(gpu_backend) = GPUBackend::new(0, 30) {
manager.register_backend("gpu", Backend::GPU(gpu_backend));
}
}
manager
}
pub fn benchmark_backends<const N: usize>(
manager: &BackendManager,
circuit: &Circuit<N>,
parameters: &[f64],
) -> Result<HashMap<String, f64>> {
let mut results = HashMap::new();
for (backend_name, _) in manager.list_backends() {
let start = std::time::Instant::now();
let _result = manager.execute_circuit(circuit, parameters, None)?;
let duration = start.elapsed().as_secs_f64();
results.insert(backend_name, duration);
}
Ok(results)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_statevector_backend() {
let backend = StatevectorBackend::new(10);
assert_eq!(backend.name(), "statevector");
assert_eq!(backend.max_qubits(), 10);
assert!(!backend.supports_noise());
}
#[test]
fn test_mps_backend() {
let backend = MPSBackend::new(64, 50);
assert_eq!(backend.name(), "mps");
assert_eq!(backend.max_qubits(), 50);
let caps = backend.capabilities();
assert!(!caps.adjoint_gradients);
assert!(!caps.gpu_acceleration);
}
#[test]
fn test_backend_manager() {
let mut manager = BackendManager::new();
manager.register_backend("test", Backend::Statevector(StatevectorBackend::new(10)));
let backends = manager.list_backends();
assert_eq!(backends.len(), 1);
assert_eq!(backends[0].0, "test");
}
#[test]
fn test_backend_selection() {
let mut manager = backend_utils::create_default_manager();
manager.set_strategy(BackendSelectionStrategy::Fastest);
let result = manager.select_backend(15, None);
assert!(result.is_ok());
let result = manager.select_backend(35, None);
assert!(result.is_ok());
}
}