1#![macro_use]
3use std::error;
4pub use std::f64::consts::PI as pi;
5use std::fmt;
6use std::result;
7
8use na::{Matrix2, Vector2};
9use num::complex::Complex;
10#[cfg(test)]
11use proptest::prelude::*;
12
13pub type ComplexMatrix = Matrix2<Complex<f64>>;
15
16pub type ComplexVector = Vector2<Complex<f64>>;
18
19pub type Result<T> = result::Result<T, JonesError>;
23
24#[derive(Debug)]
26pub enum JonesError {
27 IntensityTooLarge,
33
34 NoBeam,
36
37 NoElements,
39
40 Other(String),
42}
43
44impl error::Error for JonesError {
45 fn description(&self) -> &str {
46 ""
47 }
48
49 fn cause(&self) -> Option<&error::Error> {
50 None
51 }
52}
53
54impl fmt::Display for JonesError {
55 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56 match self {
57 JonesError::IntensityTooLarge => write!(f, "Intensity error: Intensity is too large"),
58 JonesError::Other(ref msg) => write!(f, "Other error: {}", msg),
59 JonesError::NoBeam => {
60 write!(f, "Optical system error: the system does not have a beam")
61 }
62 JonesError::NoElements => write!(f, "Optical system error: the system has no elements"),
63 }
64 }
65}
66
67#[derive(Debug, Copy, Clone, PartialEq)]
73pub enum Angle {
74 Degrees(f64),
75 Radians(f64),
76}
77
78#[cfg(test)]
79impl Arbitrary for Angle {
80 type Parameters = ();
81 type Strategy = BoxedStrategy<Self>;
82
83 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
84 prop_oneof![
85 (any::<f64>()).prop_map(|x| Angle::Degrees(x)),
86 (any::<f64>()).prop_map(|x| Angle::Radians(x)),
87 ]
88 .boxed()
89 }
90}
91
92#[derive(Debug, Copy, Clone, PartialEq, Eq)]
97pub enum Handedness {
98 Left,
99 Right,
100}
101
102#[cfg(test)]
103impl Arbitrary for Handedness {
104 type Parameters = ();
105 type Strategy = BoxedStrategy<Self>;
106
107 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
108 prop_oneof![Just(Handedness::Left), Just(Handedness::Right),].boxed()
109 }
110}
111
112#[derive(Debug, Clone, PartialEq)]
118pub enum Polarization {
119 Linear(Angle),
120 Circular(Handedness),
121 Elliptical(Complex<f64>, Complex<f64>),
122}
123
124#[cfg(test)]
125pub fn any_linear_polarization() -> impl Strategy<Value = Polarization> {
126 any::<Angle>().prop_map(|angle| Polarization::Linear(angle))
127}
128
129#[cfg(test)]
130pub fn any_circular_polarization() -> impl Strategy<Value = Polarization> {
131 any::<Handedness>().prop_map(|h| Polarization::Circular(h))
132}
133
134#[cfg(test)]
135pub fn any_elliptical_polarization() -> impl Strategy<Value = Polarization> {
136 (any::<f64>(), any::<f64>(), any::<f64>(), any::<f64>()).prop_map(|(xr, xi, yr, yi)| {
137 let x = Complex::new(xr, xi);
138 let y = Complex::new(yr, yi);
139 Polarization::Elliptical(x, y)
140 })
141}
142
143#[cfg(test)]
144#[derive(Debug, Copy, Clone, PartialEq, Eq)]
145pub enum PolarizationKind {
146 Linear,
147 Circular,
148 Elliptical,
149 Any,
150}
151
152#[cfg(test)]
153impl Default for PolarizationKind {
154 fn default() -> Self {
155 PolarizationKind::Any
156 }
157}
158
159#[cfg(test)]
160impl Arbitrary for Polarization {
161 type Parameters = PolarizationKind;
162 type Strategy = BoxedStrategy<Polarization>;
163
164 fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
165 match args {
166 PolarizationKind::Linear => any_linear_polarization().boxed(),
167 PolarizationKind::Circular => any_circular_polarization().boxed(),
168 PolarizationKind::Elliptical => any_elliptical_polarization().boxed(),
169 PolarizationKind::Any => prop_oneof![
170 any_linear_polarization(),
171 any_circular_polarization(),
172 any_elliptical_polarization(),
173 ]
174 .boxed(),
175 }
176 }
177}
178
179pub trait JonesVector {
180 fn from_polarization(pol: Polarization) -> Self;
183
184 fn intensity(&self) -> Result<f64>;
187
188 fn remove_common_phase(&self) -> Self;
192
193 fn remove_common_phase_mut(&mut self);
196
197 fn relative_phase_deg(&self) -> f64;
200
201 fn relative_phase_rad(&self) -> f64;
204
205 fn vector(&self) -> ComplexVector;
207
208 fn apply_element<T: JonesMatrix>(&self, elem: T) -> Self;
211
212 fn apply_element_mut<T: JonesMatrix>(&mut self, elem: T);
215
216 fn x(&self) -> Complex<f64>;
218
219 fn y(&self) -> Complex<f64>;
221}
222
223#[derive(Debug, Clone, PartialEq)]
225pub struct Beam {
226 vec: ComplexVector,
227}
228
229impl Beam {
230 pub fn new(x: Complex<f64>, y: Complex<f64>) -> Self {
232 Beam {
233 vec: ComplexVector::new(x, y),
234 }
235 }
236
237 pub fn linear(angle: Angle) -> Self {
240 let rad = match angle {
241 Angle::Radians(rad) => rad,
242 Angle::Degrees(deg) => deg.to_radians(),
243 };
244 let x = Complex::<f64>::new(rad.cos(), 0.0);
245 let y = Complex::<f64>::new(rad.sin(), 0.0);
246 Beam {
247 vec: ComplexVector::new(x, y),
248 }
249 }
250
251 pub fn circular(hand: Handedness) -> Self {
253 let norm = (1_f64 / 2_f64).sqrt();
254 let i = Complex::<f64>::i();
255 let x = Complex::<f64>::new(norm, 0.0);
256 let y = match hand {
257 Handedness::Left => norm * i,
258 Handedness::Right => -norm * i,
259 };
260 Beam {
261 vec: ComplexVector::new(x, y),
262 }
263 }
264
265 pub fn from_vec(v: ComplexVector) -> Self {
270 Beam { vec: v }
271 }
272}
273
274impl JonesVector for Beam {
275 fn from_polarization(pol: Polarization) -> Self {
276 use self::Polarization::*;
277
278 match pol {
279 Linear(angle) => Beam::linear(angle),
280 Circular(hand) => Beam::circular(hand),
281 Elliptical(x, y) => Beam::new(x, y),
282 }
283 }
284
285 fn intensity(&self) -> Result<f64> {
286 let conj = ComplexVector::new(self.vec.x.conj(), self.vec.y.conj());
287 let num = conj.dot(&self.vec).re;
289 if num.is_infinite() {
290 Err(JonesError::IntensityTooLarge)
291 } else {
292 Ok(num)
293 }
294 }
295
296 fn remove_common_phase(&self) -> Self {
297 let (x_mag, x_phase) = self.vec.x.to_polar();
298 let (y_mag, y_phase) = self.vec.y.to_polar();
299 let mut new_y_mag = y_mag;
300 let new_x_phase = 0.0;
301 let mut new_y_phase = y_phase - x_phase;
302 if (y_mag == 0.0) || (y_mag == -0.0) {
303 new_y_phase = 0.0;
304 new_y_mag = 0.0; } else {
306 while new_y_phase < -pi {
308 new_y_phase += 2.0 * pi;
309 }
310 while new_y_phase > pi {
311 new_y_phase -= 2.0 * pi;
312 }
313 }
314 let new_y = Complex::from_polar(&new_y_mag, &new_y_phase);
315 let new_x = Complex::<f64>::new(x_mag, new_x_phase);
316 Beam {
317 vec: ComplexVector::new(new_x, new_y),
318 }
319 }
320
321 fn remove_common_phase_mut(&mut self) {
322 let (x_mag, x_phase) = self.vec.x.to_polar();
323 let (y_mag, y_phase) = self.vec.y.to_polar();
324 let mut new_y_mag = y_mag;
325 let new_x_phase = 0.0;
326 let mut new_y_phase = y_phase - x_phase;
327 if (y_mag == 0.0) || (y_mag == -0.0) {
328 new_y_phase = 0.0;
329 new_y_mag = 0.0; } else {
331 while new_y_phase < -pi {
333 new_y_phase += 2.0 * pi;
334 }
335 while new_y_phase > pi {
336 new_y_phase -= 2.0 * pi;
337 }
338 }
339 self.vec.x = Complex::from_polar(&x_mag, &new_x_phase);
340 self.vec.y = Complex::from_polar(&new_y_mag, &new_y_phase);
341 }
342
343 fn relative_phase_deg(&self) -> f64 {
344 let (_, x_phase) = self.vec.x.to_polar();
345 let (_, y_phase) = self.vec.y.to_polar();
346 y_phase.to_degrees() - x_phase.to_degrees()
347 }
348
349 fn relative_phase_rad(&self) -> f64 {
350 let (_, x_phase) = self.vec.x.to_polar();
351 let (_, y_phase) = self.vec.y.to_polar();
352 y_phase - x_phase
353 }
354
355 fn vector(&self) -> ComplexVector {
356 self.vec
357 }
358
359 fn apply_element<T: JonesMatrix>(&self, elem: T) -> Self {
360 let vec_after = elem.matrix() * self.vec;
361 Beam::from_vec(vec_after)
362 }
363
364 fn apply_element_mut<T: JonesMatrix>(&mut self, elem: T) {
365 self.vec = elem.matrix() * self.vec;
366 }
367
368 fn x(&self) -> Complex<f64> {
369 self.vec.x
370 }
371
372 fn y(&self) -> Complex<f64> {
373 self.vec.y
374 }
375}
376
377#[cfg(test)]
378impl Arbitrary for Beam {
379 type Parameters = PolarizationKind;
380 type Strategy = BoxedStrategy<Beam>;
381
382 fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
383 match args {
384 PolarizationKind::Linear => any_linear_beam().boxed(),
385 PolarizationKind::Circular => any_circular_beam().boxed(),
386 PolarizationKind::Elliptical => any_elliptical_beam().boxed(),
387 PolarizationKind::Any => prop_oneof![
388 any_linear_beam(),
389 any_circular_beam(),
390 any_elliptical_beam(),
391 ]
392 .boxed(),
393 }
394 }
395}
396
397#[cfg(test)]
398pub fn any_linear_beam() -> impl Strategy<Value = Beam> {
399 any::<Angle>().prop_map(|angle| Beam::linear(angle))
400}
401
402#[cfg(test)]
403pub fn any_circular_beam() -> impl Strategy<Value = Beam> {
404 any::<Handedness>().prop_map(|h| Beam::circular(h))
405}
406
407#[cfg(test)]
408pub fn any_elliptical_beam() -> impl Strategy<Value = Beam> {
409 (any_complex(), any_complex()).prop_map(|(x, y)| Beam::new(x, y))
410}
411
412pub trait JonesMatrix {
413 fn rotated(&self, angle: Angle) -> Self;
415
416 fn rotate(&mut self, angle: Angle);
418
419 fn matrix(&self) -> ComplexMatrix;
421}
422
423pub fn rotate_matrix(mat: &ComplexMatrix, angle: &Angle) -> ComplexMatrix {
425 let rad = match *angle {
426 Angle::Radians(rad) => rad,
427 Angle::Degrees(deg) => deg.to_radians(),
428 };
429 let rot_mat = Matrix2::new(
430 Complex::new(rad.cos(), 0_f64),
431 Complex::new(rad.sin(), 0_f64),
432 Complex::new(-rad.sin(), 0_f64),
433 Complex::new(rad.cos(), 0_f64),
434 );
435 let rot_mat_inv = Matrix2::new(
436 Complex::new(rad.cos(), 0_f64),
437 Complex::new(-rad.sin(), 0_f64),
438 Complex::new(rad.sin(), 0_f64),
439 Complex::new(rad.cos(), 0_f64),
440 );
441 rot_mat_inv * mat * rot_mat
442}
443
444#[cfg(test)]
445pub fn any_complex() -> impl Strategy<Value = Complex<f64>> {
446 (nice_f64(), nice_f64())
447 .prop_map(|(x, y)| Complex::new(x, y))
448 .boxed()
449}
450
451#[cfg(test)]
452pub fn nice_f64() -> impl Strategy<Value = f64> {
453 (-1e3_f64..1e3_f64)
454 .prop_filter("f64 shouldn't be too big", |x| x.abs() < 1e3)
455 .prop_filter("f64 shouldn't be too small, but may be zero", |x| {
456 (x.abs() > 1e-3) || (*x == 0.0)
457 })
458}
459
460#[cfg(test)]
461pub fn float_angle() -> impl Strategy<Value = f64> {
462 (0_f64..180_f64).boxed()
463}
464
465#[cfg(test)]
466pub fn float_is_well_behaved(x: f64) -> bool {
467 let not_nan = !x.is_nan();
468 let not_too_small = x > -1e12;
469 let not_too_big = x < 1e12;
470 return not_nan && not_too_small && not_too_big;
471}
472
473#[cfg(test)]
474macro_rules! assert_complex_approx_eq {
475 ($x:expr, $y:expr) => {
476 assert_approx_eq!($x.re, $y.re);
477 assert_approx_eq!($x.im, $y.im);
478 };
479}
480
481#[cfg(test)]
482macro_rules! assert_beam_approx_eq {
483 ($x:expr, $y:expr) => {
484 let vec1 = $x.vector();
485 let vec2 = $y.vector();
486 assert_complex_approx_eq!(vec1[0], vec2[0]);
487 assert_complex_approx_eq!(vec1[1], vec2[1]);
488 };
489}
490
491#[cfg(test)]
492macro_rules! assert_matrix_approx_eq {
493 ($x:expr, $y:expr) => {
494 assert_complex_approx_eq!($x[(0, 0)], $y[(0, 0)]);
495 assert_complex_approx_eq!($x[(0, 1)], $y[(0, 1)]);
496 assert_complex_approx_eq!($x[(1, 0)], $y[(1, 0)]);
497 assert_complex_approx_eq!($x[(1, 1)], $y[(1, 1)]);
498 };
499}
500
501#[cfg(test)]
502pub fn are_rel_eq(x: f64, y: f64, frac: f64) -> bool {
503 let diff = (x - y).abs();
504 let avg = ((x + y) / 2.0).abs();
505 let mut limit = avg * frac;
506 if limit < 1e-6 {
507 limit = 1e-6;
508 }
509 diff < limit
510}
511
512#[cfg(test)]
513macro_rules! prop_assert_approx_eq {
514 ($x:expr, $y:expr) => {
515 prop_assert!(are_rel_eq($x, $y, 1e-3))
516 };
517}
518
519#[cfg(test)]
520macro_rules! prop_assert_complex_approx_eq {
521 ($x:expr, $y:expr) => {
522 prop_assert_approx_eq!($x.re, $y.re);
523 prop_assert_approx_eq!($x.im, $y.im);
524 };
525}
526
527#[cfg(test)]
528macro_rules! prop_assert_beam_approx_eq {
529 ($x:expr, $y:expr) => {
530 let vec1 = $x.vector();
531 let vec2 = $y.vector();
532 prop_assert_complex_approx_eq!(vec1[0], vec2[0]);
533 prop_assert_complex_approx_eq!(vec1[1], vec2[1]);
534 };
535}
536
537#[cfg(test)]
538macro_rules! prop_assert_matrix_approx_eq {
539 ($x:expr, $y:expr) => {
540 prop_assert_complex_approx_eq!($x[(0, 0)], $y[(0, 0)]);
541 prop_assert_complex_approx_eq!($x[(0, 1)], $y[(0, 1)]);
542 prop_assert_complex_approx_eq!($x[(1, 0)], $y[(1, 0)]);
543 prop_assert_complex_approx_eq!($x[(1, 1)], $y[(1, 1)]);
544 };
545}
546
547#[cfg(test)]
549mod test {
550 use super::*;
551
552 proptest! {
553 #[test]
554 fn test_intensity_one_real_component(n: f64) {
555 let beam = Beam::new(
556 Complex::new(n, 0_f64),
557 Complex::new(0_f64, 0_f64),
558 );
559 let intensity = beam.intensity();
560 prop_assume!(intensity.is_ok());
561 prop_assert_approx_eq!(n.powi(2), intensity.unwrap());
562 }
563
564 #[test]
565 fn test_intensity_is_never_negative(beam: Beam) {
566 prop_assume!(beam.intensity().is_ok());
567 let intensity = beam.intensity().unwrap();
568 prop_assert!(intensity >= 0.0);
569 }
570
571 #[test]
572 fn test_intensity_mag_with_complex(x in any_complex(), y in any_complex()) {
573 let xr = x.re;
574 let xi = x.im;
575 let yr = y.re;
576 let yi = y.im;
577 let by_hand = xr.powi(2) + xi.powi(2) + yr.powi(2) + yi.powi(2);
578 let beam = Beam::new(x, y);
579 prop_assume!(beam.intensity().is_ok());
580 prop_assert_approx_eq!(beam.intensity().unwrap(), by_hand);
581 }
582
583 #[test]
584 fn test_common_phase_preserves_x_mag(beam: Beam) {
585 let old_x_mag = beam.x().norm();
586 let new_beam = beam.remove_common_phase();
587 let new_x_mag = new_beam.x().norm();
588 prop_assert_eq!(old_x_mag, new_x_mag);
589 }
590
591 #[test]
592 fn test_common_phase_preserves_y_mag(beam: Beam) {
593 let old_y_mag = beam.y().norm();
594 let new_beam = beam.remove_common_phase();
595 let new_y_mag = new_beam.y().norm();
596 prop_assert!((old_y_mag - new_y_mag).abs() < 1e-6);
597 }
598
599 #[test]
600 fn test_common_phase_zeroes_x_phase(beam: Beam) {
601 let new_beam = beam.remove_common_phase();
602 let new_phase = new_beam.x().arg();
603 prop_assert!(new_phase.abs() < 1e-6);
604 }
605
606 #[test]
607 fn test_common_phase_correct_y_phase(beam: Beam) {
608 let old_y_phase = beam.y().arg();
609 let old_x_phase = beam.x().arg();
610 let new_beam = beam.remove_common_phase();
611 let new_y_phase = new_beam.y().arg();
612 let mut expected_y_phase = old_y_phase - old_x_phase;
613 if beam.y().norm() == 0.0 {
614 expected_y_phase = 0.0;
621 } else {
622 while expected_y_phase < -pi {
624 expected_y_phase = expected_y_phase + 2.0 * pi;
625 }
626 while expected_y_phase > pi {
627 expected_y_phase = expected_y_phase - 2.0 * pi;
628 }
629 }
630 if (expected_y_phase - new_y_phase).abs() > 1e-6 {
634 let new_norm = new_beam.y().norm();
635 if new_norm.abs() == 0.0 {
636 prop_assert!(new_y_phase.abs() < 1e-6);
637 }
638 } else {
639 prop_assert!((expected_y_phase - new_y_phase).abs() < 1e-6);
640 }
641 }
642
643 #[test]
644 fn test_common_phase_preserves_intensity(beam: Beam) {
645 let old_intensity = beam.intensity();
646 prop_assume!(old_intensity.is_ok());
647 let new_beam = beam.remove_common_phase();
648 let new_intensity = new_beam.intensity();
649 prop_assert!(new_intensity.is_ok());
650 prop_assert!((new_intensity.unwrap() - old_intensity.unwrap()).abs() < 1e-6);
651 }
652
653 #[test]
654 fn test_common_phase_mut_preserves_x_mag(beam: Beam) {
655 let old_x_mag = beam.x().norm();
656 beam.remove_common_phase();
657 let new_x_mag = beam.x().norm();
658 prop_assert_eq!(old_x_mag, new_x_mag);
659 }
660
661 #[test]
662 fn test_common_phase_mut_preserves_y_mag(beam: Beam) {
663 let old_y_mag = beam.y().norm();
664 beam.remove_common_phase();
665 let new_y_mag = beam.y().norm();
666 prop_assert!((old_y_mag - new_y_mag).abs() < 1e-6);
667 }
668
669 #[test]
670 fn test_common_phase_mut_zeroes_x_phase(mut beam: Beam) {
671 beam.remove_common_phase_mut();
672 let new_phase = beam.x().arg();
673 prop_assert!(new_phase.abs() < 1e-6);
674 }
675
676 #[test]
677 fn test_common_phase_mut_correct_y_phase(mut beam: Beam) {
678 let old_y_phase = beam.y().arg();
679 let old_x_phase = beam.x().arg();
680 beam.remove_common_phase_mut();
681 let new_y_phase = beam.y().arg();
682 let mut expected_y_phase = old_y_phase - old_x_phase;
683 if beam.y().norm() == 0.0 {
684 expected_y_phase = 0.0;
691 } else {
692 while expected_y_phase < - pi {
694 expected_y_phase = expected_y_phase + 2.0 * pi;
695 }
696 while expected_y_phase > pi {
697 expected_y_phase = expected_y_phase - 2.0 * pi;
698 }
699 }
700 if (expected_y_phase - new_y_phase).abs() > 1e-6 {
704 let new_norm = beam.y().norm();
705 if new_norm.abs() == 0.0 {
706 prop_assert!(new_y_phase.abs() < 1e-6);
707 }
708 } else {
709 prop_assert!((expected_y_phase - new_y_phase).abs() < 1e-6);
710 }
711 }
712
713 #[test]
714 fn test_common_phase_mut_preserves_intensity(beam: Beam) {
715 let old_intensity = beam.intensity();
716 prop_assume!(old_intensity.is_ok());
717 beam.remove_common_phase();
718 let new_intensity = beam.intensity();
719 prop_assert!(new_intensity.is_ok());
720 prop_assert!((new_intensity.unwrap() - old_intensity.unwrap()).abs() < 1e-6);
721 }
722
723
724 #[test]
725 fn test_relative_phase(x in any_complex(), y in any_complex()) {
726 let expected_rad = y.arg() - x.arg();
727 let expected_deg = expected_rad.to_degrees();
728 let beam = Beam::new(x, y);
729 prop_assert_approx_eq!(expected_rad, beam.relative_phase_rad());
730 prop_assert_approx_eq!(expected_deg, beam.relative_phase_deg());
731 }
732
733 #[test]
734 fn test_construct_beam_from_xy(x in any_complex(), y in any_complex()) {
735 let beam = Beam::new(x, y);
736 prop_assert_eq!(beam.x(), x);
737 prop_assert_eq!(beam.y(), y);
738 }
739
740 #[test]
741 fn test_rotate_360_degrees_returns_original(m00 in any_complex(),
742 m01 in any_complex(),
743 m10 in any_complex(),
744 m11 in any_complex()) {
745 let mat = Matrix2::new(m00, m01, m10, m11);
746 let angle = Angle::Degrees(360_f64);
747 let rotated = rotate_matrix(&mat, &angle);
748 prop_assert_matrix_approx_eq!(mat, rotated);
749 }
750
751 #[test]
752 fn test_rotate_2pi_rad_returns_original(m00 in any_complex(),
753 m01 in any_complex(),
754 m10 in any_complex(),
755 m11 in any_complex()) {
756 let mat = Matrix2::new(m00, m01, m10, m11);
757 let angle = Angle::Radians(2.0 * pi);
758 let rotated = rotate_matrix(&mat, &angle);
759 prop_assert_matrix_approx_eq!(mat, rotated);
760 }
761
762 #[test]
763 fn test_rotate_0_degrees_returns_original(m00 in any_complex(),
764 m01 in any_complex(),
765 m10 in any_complex(),
766 m11 in any_complex()) {
767 let mat = Matrix2::new(m00, m01, m10, m11);
768 let angle = Angle::Degrees(0_f64);
769 let rotated = rotate_matrix(&mat, &angle);
770 prop_assert_matrix_approx_eq!(mat, rotated);
771 }
772
773 #[test]
774 fn test_rotate_0_rad_returns_original(m00 in any_complex(),
775 m01 in any_complex(),
776 m10 in any_complex(),
777 m11 in any_complex()) {
778 let mat = Matrix2::new(m00, m01, m10, m11);
779 let angle = Angle::Radians(0_f64);
780 let rotated = rotate_matrix(&mat, &angle);
781 prop_assert_matrix_approx_eq!(mat, rotated);
782 }
783
784 }
785}