sciforge_lib/maths/complex/
ops.rs1use super::types::Complex;
2
3pub fn roots_of_unity(n: usize) -> Vec<Complex> {
4 (0..n)
5 .map(|k| {
6 let theta = 2.0 * std::f64::consts::PI * k as f64 / n as f64;
7 Complex::from_polar(1.0, theta)
8 })
9 .collect()
10}
11
12pub fn complex_polynomial_eval(coeffs: &[Complex], z: Complex) -> Complex {
13 let mut result = Complex::zero();
14 let mut zn = Complex::one();
15 for &c in coeffs {
16 result = result + c * zn;
17 zn = zn * z;
18 }
19 result
20}
21
22pub fn mandelbrot_iterate(c: Complex, max_iter: u32) -> u32 {
23 let mut z = Complex::zero();
24 for i in 0..max_iter {
25 if z.norm_sq() > 4.0 {
26 return i;
27 }
28 z = z * z + c;
29 }
30 max_iter
31}
32
33pub fn julia_iterate(z0: Complex, c: Complex, max_iter: u32) -> u32 {
34 let mut z = z0;
35 for i in 0..max_iter {
36 if z.norm_sq() > 4.0 {
37 return i;
38 }
39 z = z * z + c;
40 }
41 max_iter
42}
43
44pub fn newton_fractal_step(z: Complex, coeffs: &[Complex], deriv_coeffs: &[Complex]) -> Complex {
45 let fz = complex_polynomial_eval(coeffs, z);
46 let dfz = complex_polynomial_eval(deriv_coeffs, z);
47 if dfz.norm_sq() < 1e-30 {
48 return z;
49 }
50 z - fz / dfz
51}
52
53pub fn complex_matrix_mul(a: &[Vec<Complex>], b: &[Vec<Complex>]) -> Vec<Vec<Complex>> {
54 let m = a.len();
55 let n = b[0].len();
56 let mut result = vec![vec![Complex::zero(); n]; m];
57 for (i, ri) in result.iter_mut().enumerate() {
58 for (j, rij) in ri.iter_mut().enumerate() {
59 for (k, &aik) in a[i].iter().enumerate() {
60 *rij = *rij + aik * b[k][j];
61 }
62 }
63 }
64 result
65}
66
67pub fn complex_matrix_det(m: &[Vec<Complex>]) -> Complex {
68 let n = m.len();
69 if n == 1 {
70 return m[0][0];
71 }
72 if n == 2 {
73 return m[0][0] * m[1][1] - m[0][1] * m[1][0];
74 }
75 let mut det = Complex::zero();
76 for j in 0..n {
77 let sign = if j % 2 == 0 {
78 Complex::one()
79 } else {
80 -Complex::one()
81 };
82 let minor: Vec<Vec<Complex>> = (1..n)
83 .map(|i| (0..n).filter(|&k| k != j).map(|k| m[i][k]).collect())
84 .collect();
85 det = det + sign * m[0][j] * complex_matrix_det(&minor);
86 }
87 det
88}
89
90pub fn complex_exp(z: Complex) -> Complex {
91 let r = z.re.exp();
92 Complex::new(r * z.im.cos(), r * z.im.sin())
93}
94
95pub fn complex_log(z: Complex) -> Complex {
96 Complex::new(z.norm().ln(), z.im.atan2(z.re))
97}
98
99pub fn complex_sin(z: Complex) -> Complex {
100 Complex::new(z.re.sin() * z.im.cosh(), z.re.cos() * z.im.sinh())
101}
102
103pub fn complex_cos(z: Complex) -> Complex {
104 Complex::new(z.re.cos() * z.im.cosh(), -z.re.sin() * z.im.sinh())
105}
106
107pub fn complex_tan(z: Complex) -> Complex {
108 let s = complex_sin(z);
109 let c = complex_cos(z);
110 if c.norm_sq() < 1e-30 {
111 return Complex::new(f64::INFINITY, 0.0);
112 }
113 s / c
114}
115
116pub fn complex_sinh(z: Complex) -> Complex {
117 Complex::new(z.re.sinh() * z.im.cos(), z.re.cosh() * z.im.sin())
118}
119
120pub fn complex_cosh(z: Complex) -> Complex {
121 Complex::new(z.re.cosh() * z.im.cos(), z.re.sinh() * z.im.sin())
122}
123
124pub fn complex_sqrt(z: Complex) -> Complex {
125 let r = z.norm();
126 let theta = z.im.atan2(z.re);
127 Complex::from_polar(r.sqrt(), theta / 2.0)
128}
129
130pub fn complex_power(z: Complex, w: Complex) -> Complex {
131 if z.norm_sq() < 1e-60 {
132 return Complex::zero();
133 }
134 complex_exp(w * complex_log(z))
135}
136
137pub fn complex_power_real(z: Complex, n: f64) -> Complex {
138 let r = z.norm();
139 let theta = z.im.atan2(z.re);
140 Complex::from_polar(r.powf(n), n * theta)
141}
142
143pub fn mobius_transform(z: Complex, a: Complex, b: Complex, c: Complex, d: Complex) -> Complex {
144 let num = a * z + b;
145 let den = c * z + d;
146 if den.norm_sq() < 1e-60 {
147 return Complex::new(f64::INFINITY, 0.0);
148 }
149 num / den
150}
151
152pub fn bilinear_transform(s: Complex, t_sample: f64) -> Complex {
153 let one = Complex::one();
154 let half_t = Complex::new(t_sample * 0.5, 0.0);
155 let num = one + s * half_t;
156 let den = one - s * half_t;
157 if den.norm_sq() < 1e-60 {
158 return Complex::new(f64::INFINITY, 0.0);
159 }
160 num / den
161}
162
163pub fn complex_contour_integral(f: impl Fn(Complex) -> Complex, path: &[Complex]) -> Complex {
164 let mut result = Complex::zero();
165 for i in 1..path.len() {
166 let dz = path[i] - path[i - 1];
167 let mid = Complex::new(
168 (path[i].re + path[i - 1].re) * 0.5,
169 (path[i].im + path[i - 1].im) * 0.5,
170 );
171 result = result + f(mid) * dz;
172 }
173 result
174}
175
176pub fn complex_conjugate_transpose(m: &[Vec<Complex>]) -> Vec<Vec<Complex>> {
177 let rows = m.len();
178 let cols = if rows > 0 { m[0].len() } else { 0 };
179 let mut result = vec![vec![Complex::zero(); rows]; cols];
180 for (i, mi) in m.iter().enumerate() {
181 for (j, &mij) in mi.iter().enumerate() {
182 result[j][i] = mij.conj();
183 }
184 }
185 result
186}
187
188pub fn complex_matrix_trace(m: &[Vec<Complex>]) -> Complex {
189 let mut trace = Complex::zero();
190 for (i, mi) in m.iter().enumerate() {
191 trace = trace + mi[i];
192 }
193 trace
194}
195
196pub fn complex_matrix_add(a: &[Vec<Complex>], b: &[Vec<Complex>]) -> Vec<Vec<Complex>> {
197 a.iter()
198 .zip(b)
199 .map(|(ra, rb)| ra.iter().zip(rb).map(|(&x, &y)| x + y).collect())
200 .collect()
201}
202
203pub fn complex_matrix_scale(m: &[Vec<Complex>], s: Complex) -> Vec<Vec<Complex>> {
204 m.iter()
205 .map(|row| row.iter().map(|&x| x * s).collect())
206 .collect()
207}
208
209pub fn complex_dft(input: &[Complex]) -> Vec<Complex> {
210 let n = input.len();
211 (0..n)
212 .map(|k| {
213 let mut sum = Complex::zero();
214 for (j, &x) in input.iter().enumerate() {
215 let angle = -2.0 * std::f64::consts::PI * k as f64 * j as f64 / n as f64;
216 sum = sum + x * Complex::from_polar(1.0, angle);
217 }
218 sum
219 })
220 .collect()
221}
222
223pub fn complex_idft(input: &[Complex]) -> Vec<Complex> {
224 let n = input.len();
225 let scale = 1.0 / n as f64;
226 (0..n)
227 .map(|k| {
228 let mut sum = Complex::zero();
229 for (j, &x) in input.iter().enumerate() {
230 let angle = 2.0 * std::f64::consts::PI * k as f64 * j as f64 / n as f64;
231 sum = sum + x * Complex::from_polar(1.0, angle);
232 }
233 sum.scale(scale)
234 })
235 .collect()
236}