use crate::{
error::{QuantRS2Error, QuantRS2Result},
qubit::QubitId,
};
use scirs2_core::ndarray::Array2;
use scirs2_core::Complex64;
use super::{GpuBackend, GpuBuffer, GpuKernel};
#[inline(always)]
fn metal_unavailable(context: &str) -> QuantRS2Error {
QuantRS2Error::UnsupportedOperation(format!(
"Metal backend not available in this build: {context}. \
Enable the `metal` feature and ensure this code runs on macOS 10.13+ or iOS 8+."
))
}
pub struct MetalBuffer {
size_elements: usize,
}
impl GpuBuffer for MetalBuffer {
fn size(&self) -> usize {
self.size_elements * std::mem::size_of::<Complex64>()
}
fn upload(&mut self, _data: &[Complex64]) -> QuantRS2Result<()> {
Err(metal_unavailable("upload to MTLBuffer"))
}
fn download(&self, _data: &mut [Complex64]) -> QuantRS2Result<()> {
Err(metal_unavailable("download from MTLBuffer"))
}
fn sync(&self) -> QuantRS2Result<()> {
Err(metal_unavailable(
"MTLCommandBuffer commit/waitUntilCompleted",
))
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
pub struct MetalKernel;
impl GpuKernel for MetalKernel {
fn apply_single_qubit_gate(
&self,
_state: &mut dyn GpuBuffer,
_gate_matrix: &[Complex64; 4],
_qubit: QubitId,
_n_qubits: usize,
) -> QuantRS2Result<()> {
Err(metal_unavailable("apply_single_qubit_gate"))
}
fn apply_two_qubit_gate(
&self,
_state: &mut dyn GpuBuffer,
_gate_matrix: &[Complex64; 16],
_control: QubitId,
_target: QubitId,
_n_qubits: usize,
) -> QuantRS2Result<()> {
Err(metal_unavailable("apply_two_qubit_gate"))
}
fn apply_multi_qubit_gate(
&self,
_state: &mut dyn GpuBuffer,
_gate_matrix: &Array2<Complex64>,
_qubits: &[QubitId],
_n_qubits: usize,
) -> QuantRS2Result<()> {
Err(metal_unavailable("apply_multi_qubit_gate"))
}
fn measure_qubit(
&self,
_state: &dyn GpuBuffer,
_qubit: QubitId,
_n_qubits: usize,
) -> QuantRS2Result<(bool, f64)> {
Err(metal_unavailable("measure_qubit"))
}
fn expectation_value(
&self,
_state: &dyn GpuBuffer,
_observable: &Array2<Complex64>,
_qubits: &[QubitId],
_n_qubits: usize,
) -> QuantRS2Result<f64> {
Err(metal_unavailable("expectation_value"))
}
}
pub struct MetalBackend {
kernel: MetalKernel,
}
impl MetalBackend {
pub fn new() -> QuantRS2Result<Self> {
eprintln!(
"[quantrs2-core] WARNING: Metal backend requested but Metal runtime is not available. \
This backend requires macOS 10.13+ or iOS 8+ with the `metal` feature enabled."
);
Err(QuantRS2Error::UnsupportedOperation(
"Metal backend not available in this build. \
Compile with the `metal` feature on a supported Apple platform."
.to_string(),
))
}
}
impl GpuBackend for MetalBackend {
fn is_available() -> bool {
false
}
fn name(&self) -> &'static str {
"Metal"
}
fn device_info(&self) -> String {
"Metal backend (stub — no runtime available)".to_string()
}
fn allocate_state_vector(&self, n_qubits: usize) -> QuantRS2Result<Box<dyn GpuBuffer>> {
eprintln!(
"[quantrs2-core] WARNING: MetalBackend::allocate_state_vector called for {n_qubits} qubits \
but Metal runtime is not available."
);
Err(metal_unavailable(&format!(
"allocate_state_vector for {n_qubits} qubits"
)))
}
fn allocate_density_matrix(&self, n_qubits: usize) -> QuantRS2Result<Box<dyn GpuBuffer>> {
eprintln!(
"[quantrs2-core] WARNING: MetalBackend::allocate_density_matrix called for {n_qubits} qubits \
but Metal runtime is not available."
);
Err(metal_unavailable(&format!(
"allocate_density_matrix for {n_qubits} qubits"
)))
}
fn kernel(&self) -> &dyn GpuKernel {
&self.kernel
}
}