1use scirs2_core::numeric::Float;
16use std::sync::Arc;
17use torsh_core::sync::RwLockExt;
18use torsh_core::{
19 dtype::{ComplexElement, TensorElement},
20 error::{Result, TorshError},
21};
22
23use crate::core_ops::{Operation, Tensor};
24
25impl<T: ComplexElement + Copy> Tensor<T> {
26 pub fn complex_conj(&self) -> Result<Self>
28 where
29 T: Copy,
30 {
31 let data = self.to_vec()?;
32 let conj_data: Vec<T> = data.iter().map(|&z| z.conj()).collect();
33 let mut result = Self::from_data(conj_data, self.shape().dims().to_vec(), self.device)?;
34 result.requires_grad = crate::should_record_grad(self.requires_grad);
35
36 if crate::should_record_grad(self.requires_grad) {
38 result.operation = Operation::Custom(
39 "complex_conj".to_string(),
40 vec![Arc::downgrade(&Arc::new(self.clone()))],
41 );
42 }
43
44 Ok(result)
45 }
46
47 pub fn real(&self) -> Result<Tensor<T::Real>>
49 where
50 T::Real: TensorElement + Copy,
51 {
52 let data = self.to_vec()?;
53 let real_data: Vec<T::Real> = data.iter().map(|x| x.real()).collect();
54 Tensor::from_data(real_data, self.shape().dims().to_vec(), self.device)
55 }
56
57 pub fn imag(&self) -> Result<Tensor<T::Real>>
59 where
60 T::Real: TensorElement + Copy,
61 {
62 let data = self.to_vec()?;
63 let imag_data: Vec<T::Real> = data.iter().map(|x| x.imag()).collect();
64 Tensor::from_data(imag_data, self.shape().dims().to_vec(), self.device)
65 }
66
67 pub fn abs(&self) -> Result<Tensor<T::Real>>
78 where
79 T::Real: TensorElement + Copy + num_traits::Float,
80 {
81 let data = self.to_vec()?;
82 let abs_data: Vec<T::Real> = data.iter().map(|x| x.abs()).collect();
83 let result = Tensor::from_data(abs_data, self.shape().dims().to_vec(), self.device)?;
84 Ok(self.record_abs_if_real(result))
85 }
86
87 pub fn angle(&self) -> Result<Tensor<T::Real>>
89 where
90 T::Real: TensorElement + Copy + num_traits::Float,
91 {
92 let data = self.to_vec()?;
93 let angle_data: Vec<T::Real> = data.iter().map(|x| x.arg()).collect();
94 Tensor::from_data(angle_data, self.shape().dims().to_vec(), self.device)
95 }
96
97 pub fn complex(real: &Tensor<T::Real>, imag: &Tensor<T::Real>) -> Result<Self>
99 where
100 T::Real: TensorElement + Copy,
101 {
102 if real.shape() != imag.shape() {
103 return Err(TorshError::ShapeMismatch {
104 expected: real.shape().dims().to_vec(),
105 got: imag.shape().dims().to_vec(),
106 });
107 }
108
109 let real_data = real.to_vec()?;
110 let imag_data = imag.to_vec()?;
111
112 let complex_data: Vec<T> = real_data
113 .iter()
114 .zip(imag_data.iter())
115 .map(|(&r, &i)| T::new(r, i))
116 .collect();
117
118 Self::from_data(complex_data, real.shape().dims().to_vec(), real.device)
119 }
120
121 pub fn polar(magnitude: &Tensor<T::Real>, phase: &Tensor<T::Real>) -> Result<Self>
123 where
124 T::Real: TensorElement + Copy + num_traits::Float,
125 {
126 if magnitude.shape() != phase.shape() {
127 return Err(TorshError::ShapeMismatch {
128 expected: magnitude.shape().dims().to_vec(),
129 got: phase.shape().dims().to_vec(),
130 });
131 }
132
133 let mag_data = magnitude.to_vec()?;
134 let phase_data = phase.to_vec()?;
135
136 let complex_data: Vec<T> = mag_data
137 .iter()
138 .zip(phase_data.iter())
139 .map(|(&mag, &phase)| {
140 let real = mag * phase.cos();
141 let imag = mag * phase.sin();
142 T::new(real, imag)
143 })
144 .collect();
145
146 Self::from_data(
147 complex_data,
148 magnitude.shape().dims().to_vec(),
149 magnitude.device,
150 )
151 }
152
153 pub fn backward_complex(&self) -> Result<()>
158 where
159 T: Copy
160 + Default
161 + std::ops::Add<Output = T>
162 + std::ops::Sub<Output = T>
163 + std::ops::Mul<Output = T>
164 + std::ops::Div<Output = T>,
165 {
166 if !self.requires_grad {
167 return Err(TorshError::AutogradError(
168 "Called backward on tensor that doesn't require grad".to_string(),
169 ));
170 }
171
172 if self.shape().numel() != 1 {
173 return Err(TorshError::AutogradError(
174 "Gradient can only be computed for scalar outputs".to_string(),
175 ));
176 }
177
178 let output_grad_data = vec![T::new(
180 <T::Real as TensorElement>::one(),
181 <T::Real as TensorElement>::zero(),
182 )];
183 let output_grad = Self::from_data(output_grad_data, vec![], self.device)?;
184
185 self.backward_complex_impl(&output_grad)?;
187
188 Ok(())
189 }
190
191 fn backward_complex_impl(&self, grad_output: &Self) -> Result<()>
193 where
194 T: Copy
195 + Default
196 + std::ops::Add<Output = T>
197 + std::ops::Sub<Output = T>
198 + std::ops::Mul<Output = T>
199 + std::ops::Div<Output = T>,
200 {
201 match &self.operation {
202 Operation::Leaf => {
203 let mut grad_lock = self.grad.write_or_recover();
205 if let Some(existing_grad) = grad_lock.as_ref() {
206 let new_grad = existing_grad.add_op(grad_output)?;
208 *grad_lock = Some(new_grad);
209 } else {
210 *grad_lock = Some(grad_output.clone());
212 }
213 }
214 Operation::Add { lhs, rhs } => {
215 if lhs.requires_grad {
217 lhs.backward_complex_impl(grad_output)?;
218 }
219 if rhs.requires_grad {
220 rhs.backward_complex_impl(grad_output)?;
221 }
222 }
223 Operation::Mul { lhs, rhs } => {
224 if lhs.requires_grad {
226 let lhs_grad = (**rhs).mul_op(grad_output)?;
227 lhs.backward_complex_impl(&lhs_grad)?;
228 }
229 if rhs.requires_grad {
230 let rhs_grad = (**lhs).mul_op(grad_output)?;
231 rhs.backward_complex_impl(&rhs_grad)?;
232 }
233 }
234 Operation::Custom(op_name, inputs) => {
235 match op_name.as_str() {
236 "complex_conj" => {
237 if let Some(weak_input) = inputs.first() {
239 if let Some(input) = weak_input.upgrade() {
240 if input.requires_grad {
241 let conj_grad = grad_output.complex_conj()?;
242 input.backward_complex_impl(&conj_grad)?;
243 }
244 }
245 }
246 }
247 "complex_abs" => {
248 if let Some(weak_input) = inputs.first() {
250 if let Some(input) = weak_input.upgrade() {
251 if input.requires_grad {
252 let input_data = input.to_vec()?;
253 let grad_data = grad_output.to_vec()?;
254
255 let input_grad_data: Vec<T> = input_data
256 .iter()
257 .zip(grad_data.iter())
258 .map(|(&z, &grad)| {
259 let abs_z = z.abs();
260 if abs_z > T::Real::zero() {
261 let z_normalized =
263 T::new(z.real() / abs_z, z.imag() / abs_z);
264 T::new(
265 z_normalized.real() * grad.real()
266 - z_normalized.imag() * grad.imag(),
267 z_normalized.real() * grad.imag()
268 + z_normalized.imag() * grad.real(),
269 )
270 } else {
271 T::new(T::Real::zero(), T::Real::zero())
272 }
273 })
274 .collect();
275
276 let input_grad = Self::from_data(
277 input_grad_data,
278 input.shape().dims().to_vec(),
279 input.device,
280 )?;
281 input.backward_complex_impl(&input_grad)?;
282 }
283 }
284 }
285 }
286 _ => {
287 for weak_input in inputs {
289 if let Some(input) = weak_input.upgrade() {
290 if input.requires_grad {
291 input.backward_complex_impl(grad_output)?;
292 }
293 }
294 }
295 }
296 }
297 }
298 _ => {
299 }
304 }
305
306 Ok(())
307 }
308
309 pub fn complex_mul(&self, other: &Self) -> Result<Self>
311 where
312 T: std::ops::Mul<Output = T> + std::ops::Add<Output = T> + std::ops::Sub<Output = T>,
313 {
314 if self.shape() != other.shape() {
315 return Err(TorshError::ShapeMismatch {
316 expected: self.shape().dims().to_vec(),
317 got: other.shape().dims().to_vec(),
318 });
319 }
320
321 let self_data = self.to_vec()?;
322 let other_data = other.to_vec()?;
323
324 let result_data: Vec<T> = self_data
325 .iter()
326 .zip(other_data.iter())
327 .map(|(&a, &b)| {
328 T::new(
330 a.real() * b.real() - a.imag() * b.imag(),
331 a.real() * b.imag() + a.imag() * b.real(),
332 )
333 })
334 .collect();
335
336 let mut result = Self::from_data(result_data, self.shape().dims().to_vec(), self.device)?;
337
338 if crate::should_record_grad(self.requires_grad || other.requires_grad) {
340 result.requires_grad = true;
341 result.operation = Operation::Mul {
342 lhs: Arc::new(self.clone()),
343 rhs: Arc::new(other.clone()),
344 };
345 }
346
347 Ok(result)
348 }
349
350 pub fn complex_add(&self, other: &Self) -> Result<Self>
352 where
353 T: std::ops::Add<Output = T>,
354 {
355 if self.shape() != other.shape() {
356 return Err(TorshError::ShapeMismatch {
357 expected: self.shape().dims().to_vec(),
358 got: other.shape().dims().to_vec(),
359 });
360 }
361
362 let self_data = self.to_vec()?;
363 let other_data = other.to_vec()?;
364
365 let result_data: Vec<T> = self_data
366 .iter()
367 .zip(other_data.iter())
368 .map(|(&a, &b)| T::new(a.real() + b.real(), a.imag() + b.imag()))
369 .collect();
370
371 let mut result = Self::from_data(result_data, self.shape().dims().to_vec(), self.device)?;
372
373 if crate::should_record_grad(self.requires_grad || other.requires_grad) {
375 result.requires_grad = true;
376 result.operation = Operation::Add {
377 lhs: Arc::new(self.clone()),
378 rhs: Arc::new(other.clone()),
379 };
380 }
381
382 Ok(result)
383 }
384
385 pub fn is_real(&self) -> Result<bool>
387 where
388 T::Real: PartialEq + num_traits::Zero,
389 {
390 let data = self.to_vec()?;
391 Ok(data.iter().all(|&z| z.imag() == T::Real::zero()))
392 }
393
394 pub fn is_complex(&self) -> Result<bool>
396 where
397 T::Real: PartialEq + num_traits::Zero,
398 {
399 Ok(!self.is_real()?)
400 }
401}
402
403#[cfg(test)]
406mod tests {
407 use super::*;
408 use num_complex::Complex32;
409 use torsh_core::device::DeviceType;
410
411 type C32 = Complex32;
412
413 #[test]
414 fn test_complex_conjugate() {
415 let data = vec![C32::new(1.0, 2.0), C32::new(3.0, -4.0), C32::new(-1.0, 1.0)];
416 let tensor =
417 Tensor::from_data(data, vec![3], DeviceType::Cpu).expect("operation should succeed");
418
419 let conj_tensor = tensor
420 .complex_conj()
421 .expect("complex conjugate should succeed");
422 let conj_data = conj_tensor.to_vec().expect("to_vec should succeed");
423
424 assert_eq!(conj_data[0], C32::new(1.0, -2.0));
425 assert_eq!(conj_data[1], C32::new(3.0, 4.0));
426 assert_eq!(conj_data[2], C32::new(-1.0, -1.0));
427 }
428
429 #[test]
430 fn test_real_imag_extraction() {
431 let data = vec![C32::new(1.0, 2.0), C32::new(3.0, -4.0)];
432 let tensor =
433 Tensor::from_data(data, vec![2], DeviceType::Cpu).expect("operation should succeed");
434
435 let real_part = tensor.real().expect("real extraction should succeed");
436 let imag_part = tensor.imag().expect("imag extraction should succeed");
437
438 assert_eq!(
439 real_part.to_vec().expect("to_vec should succeed"),
440 vec![1.0, 3.0]
441 );
442 assert_eq!(
443 imag_part.to_vec().expect("to_vec should succeed"),
444 vec![2.0, -4.0]
445 );
446 }
447
448 #[test]
449 fn test_magnitude_and_phase() {
450 let data = vec![
451 C32::new(3.0, 4.0), C32::new(1.0, 0.0), ];
454 let tensor =
455 Tensor::from_data(data, vec![2], DeviceType::Cpu).expect("operation should succeed");
456
457 let magnitude = tensor.abs().expect("abs computation should succeed");
458 let phase = tensor.angle().expect("angle computation should succeed");
459
460 let mag_data = magnitude.to_vec().expect("to_vec should succeed");
461 let phase_data = phase.to_vec().expect("to_vec should succeed");
462
463 assert!((mag_data[0] - 5.0).abs() < 1e-6);
464 assert!((mag_data[1] - 1.0).abs() < 1e-6);
465 assert!((phase_data[1] - 0.0).abs() < 1e-6);
466 }
467
468 #[test]
469 fn test_complex_from_components() {
470 let real_data = vec![1.0f32, 2.0, 3.0];
471 let imag_data = vec![4.0f32, 5.0, 6.0];
472
473 let real_tensor = Tensor::from_data(real_data, vec![3], DeviceType::Cpu)
474 .expect("operation should succeed");
475 let imag_tensor = Tensor::from_data(imag_data, vec![3], DeviceType::Cpu)
476 .expect("operation should succeed");
477
478 let complex_tensor =
479 Tensor::<C32>::complex(&real_tensor, &imag_tensor).expect("operation should succeed");
480 let result_data = complex_tensor.to_vec().expect("to_vec should succeed");
481
482 assert_eq!(result_data[0], C32::new(1.0, 4.0));
483 assert_eq!(result_data[1], C32::new(2.0, 5.0));
484 assert_eq!(result_data[2], C32::new(3.0, 6.0));
485 }
486
487 #[test]
488 fn test_complex_arithmetic() {
489 let a_data = vec![C32::new(1.0, 2.0), C32::new(3.0, 4.0)];
490 let b_data = vec![C32::new(2.0, 1.0), C32::new(1.0, -1.0)];
491
492 let a =
493 Tensor::from_data(a_data, vec![2], DeviceType::Cpu).expect("operation should succeed");
494 let b =
495 Tensor::from_data(b_data, vec![2], DeviceType::Cpu).expect("operation should succeed");
496
497 let sum = a.complex_add(&b).expect("operation should succeed");
499 let sum_data = sum.to_vec().expect("to_vec should succeed");
500 assert_eq!(sum_data[0], C32::new(3.0, 3.0));
501 assert_eq!(sum_data[1], C32::new(4.0, 3.0));
502
503 let product = a.complex_mul(&b).expect("operation should succeed");
505 let prod_data = product.to_vec().expect("to_vec should succeed");
506 assert_eq!(prod_data[0], C32::new(0.0, 5.0));
508 assert_eq!(prod_data[1], C32::new(7.0, 1.0));
510 }
511
512 #[test]
513 fn test_polar_construction() {
514 let mag_data = vec![1.0f32, 2.0];
515 let phase_data = vec![0.0f32, std::f32::consts::PI / 2.0];
516
517 let mag_tensor = Tensor::from_data(mag_data, vec![2], DeviceType::Cpu)
518 .expect("operation should succeed");
519 let phase_tensor = Tensor::from_data(phase_data, vec![2], DeviceType::Cpu)
520 .expect("operation should succeed");
521
522 let complex_tensor =
523 Tensor::<C32>::polar(&mag_tensor, &phase_tensor).expect("operation should succeed");
524 let result_data = complex_tensor.to_vec().expect("to_vec should succeed");
525
526 assert!((result_data[0].re - 1.0).abs() < 1e-6);
528 assert!((result_data[0].im - 0.0).abs() < 1e-6);
529
530 assert!((result_data[1].re - 0.0).abs() < 1e-6);
532 assert!((result_data[1].im - 2.0).abs() < 1e-6);
533 }
534
535 #[test]
536 fn test_is_real_complex() {
537 let real_data = vec![C32::new(1.0, 0.0), C32::new(2.0, 0.0)];
538 let complex_data = vec![C32::new(1.0, 1.0), C32::new(2.0, 0.0)];
539
540 let real_tensor = Tensor::from_data(real_data, vec![2], DeviceType::Cpu)
541 .expect("operation should succeed");
542 let complex_tensor = Tensor::from_data(complex_data, vec![2], DeviceType::Cpu)
543 .expect("operation should succeed");
544
545 assert!(real_tensor.is_real().expect("is_real check should succeed"));
546 assert!(!real_tensor
547 .is_complex()
548 .expect("is_complex check should succeed"));
549
550 assert!(!complex_tensor
551 .is_real()
552 .expect("is_real check should succeed"));
553 assert!(complex_tensor
554 .is_complex()
555 .expect("is_complex check should succeed"));
556 }
557
558 #[test]
559 fn test_shape_mismatch_errors() {
560 let a = Tensor::<C32>::zeros(&[2], DeviceType::Cpu).expect("operation should succeed");
561 let b = Tensor::<C32>::zeros(&[3], DeviceType::Cpu).expect("operation should succeed");
562
563 assert!(a.complex_add(&b).is_err());
564 assert!(a.complex_mul(&b).is_err());
565
566 let real_2 = Tensor::<f32>::zeros(&[2], DeviceType::Cpu).expect("operation should succeed");
567 let imag_3 = Tensor::<f32>::zeros(&[3], DeviceType::Cpu).expect("operation should succeed");
568
569 assert!(Tensor::<C32>::complex(&real_2, &imag_3).is_err());
570 }
571}