1use super::{gradient::GradientOperations, utils::MatrixUtils};
4use crate::calculus::derivatives::Derivative;
5use crate::core::{Expression, Symbol};
6use crate::simplify::Simplify;
7use std::collections::HashMap;
8
9pub struct JacobianOperations;
11
12impl JacobianOperations {
13 pub fn compute(functions: &[Expression], variables: &[Symbol]) -> Vec<Vec<Expression>> {
36 let m = functions.len();
37 let mut jacobian = Vec::with_capacity(m);
38
39 for function in functions {
40 let gradient = GradientOperations::compute(function, variables.to_vec());
41 jacobian.push(gradient);
42 }
43
44 jacobian
45 }
46
47 pub fn compute_cached(
68 functions: &[Expression],
69 variables: &[Symbol],
70 cache: &mut HashMap<String, Expression>,
71 ) -> Vec<Vec<Expression>> {
72 let m = functions.len();
73 let n = variables.len();
74 let mut jacobian = Vec::with_capacity(m);
75
76 for (func_idx, function) in functions.iter().enumerate() {
77 let mut gradient_row = Vec::with_capacity(n);
78
79 for (var_idx, var) in variables.iter().enumerate() {
80 let cache_key = format!("f{}_{}", func_idx, var_idx);
81 let partial = cache
82 .entry(cache_key)
83 .or_insert_with(|| function.derivative(var.clone()).simplify())
84 .clone();
85 gradient_row.push(partial);
86 }
87
88 jacobian.push(gradient_row);
89 }
90
91 jacobian
92 }
93
94 pub fn transpose(functions: &[Expression], variables: &[Symbol]) -> Vec<Vec<Expression>> {
113 let jacobian = Self::compute(functions, variables);
114 Self::matrix_transpose(&jacobian)
115 }
116
117 fn matrix_transpose(matrix: &[Vec<Expression>]) -> Vec<Vec<Expression>> {
119 if matrix.is_empty() || matrix[0].is_empty() {
120 return Vec::new();
121 }
122
123 let rows = matrix.len();
124 let cols = matrix[0].len();
125
126 (0..cols)
127 .map(|j| (0..rows).map(|i| matrix[i][j].clone()).collect())
128 .collect()
129 }
130}
131
132pub struct JacobianDeterminant;
134
135impl JacobianDeterminant {
136 pub fn compute(functions: &[Expression], variables: &[Symbol]) -> Expression {
155 if functions.len() != variables.len() {
156 panic!(
157 "Jacobian determinant requires square matrix: {} functions vs {} variables",
158 functions.len(),
159 variables.len()
160 );
161 }
162
163 let jacobian_matrix = JacobianOperations::compute(functions, variables);
164 MatrixUtils::determinant(&jacobian_matrix)
165 }
166
167 pub fn absolute(functions: &[Expression], variables: &[Symbol]) -> Expression {
192 let det = Self::compute(functions, variables);
193 Expression::function("abs", vec![det])
194 }
195
196 pub fn is_singular(functions: &[Expression], variables: &[Symbol]) -> bool {
215 let det = Self::compute(functions, variables);
216 super::utils::PartialUtils::is_zero(&det)
217 }
218
219 pub fn condition_estimate(functions: &[Expression], variables: &[Symbol]) -> Expression {
247 let det = Self::compute(functions, variables);
248 let abs_det = Expression::function("abs", vec![det]);
249
250 Expression::pow(abs_det, Expression::integer(-1))
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 use crate::symbol;
258
259 #[test]
260 fn test_linear_transformation_jacobian() {
261 let x = symbol!(x);
262 let y = symbol!(y);
263
264 let functions = vec![
265 Expression::add(vec![
266 Expression::mul(vec![Expression::integer(2), Expression::symbol(x.clone())]),
267 Expression::mul(vec![Expression::integer(3), Expression::symbol(y.clone())]),
268 ]),
269 Expression::add(vec![
270 Expression::symbol(x.clone()),
271 Expression::mul(vec![Expression::integer(4), Expression::symbol(y.clone())]),
272 ]),
273 ];
274
275 let variables = vec![x.clone(), y.clone()];
276 let jacobian = JacobianOperations::compute(&functions, &variables);
277
278 assert_eq!(jacobian.len(), 2);
279 assert_eq!(jacobian[0].len(), 2);
280 assert_eq!(jacobian[1].len(), 2);
281
282 assert_eq!(jacobian[0][0].simplify(), Expression::integer(2));
283 assert_eq!(jacobian[0][1].simplify(), Expression::integer(3));
284 assert_eq!(jacobian[1][0].simplify(), Expression::integer(1));
285 assert_eq!(jacobian[1][1].simplify(), Expression::integer(4));
286 }
287
288 #[test]
289 fn test_nonlinear_transformation_jacobian() {
290 let x = symbol!(x);
291 let y = symbol!(y);
292
293 let functions = vec![
294 Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
295 Expression::mul(vec![
296 Expression::symbol(x.clone()),
297 Expression::symbol(y.clone()),
298 ]),
299 ];
300
301 let variables = vec![x.clone(), y.clone()];
302 let jacobian = JacobianOperations::compute(&functions, &variables);
303
304 assert_eq!(jacobian.len(), 2);
305 assert_eq!(jacobian[0].len(), 2);
306 assert_eq!(jacobian[1].len(), 2);
307
308 assert!(!jacobian[0][0].is_zero());
309 assert_eq!(jacobian[0][1].simplify(), Expression::integer(0));
310 assert!(!jacobian[1][0].is_zero());
311 assert!(!jacobian[1][1].is_zero());
312 }
313
314 #[test]
315 fn test_polar_to_cartesian_jacobian() {
316 let r = symbol!(r);
317 let theta = symbol!(theta);
318
319 let functions = vec![
320 Expression::mul(vec![
321 Expression::symbol(r.clone()),
322 Expression::function("cos", vec![Expression::symbol(theta.clone())]),
323 ]),
324 Expression::mul(vec![
325 Expression::symbol(r.clone()),
326 Expression::function("sin", vec![Expression::symbol(theta.clone())]),
327 ]),
328 ];
329
330 let variables = vec![r.clone(), theta.clone()];
331 let jacobian = JacobianOperations::compute(&functions, &variables);
332
333 assert_eq!(jacobian.len(), 2);
334 assert_eq!(jacobian[0].len(), 2);
335 assert_eq!(jacobian[1].len(), 2);
336
337 assert!(!jacobian[0][0].is_zero());
338 assert!(!jacobian[0][1].is_zero());
339 assert!(!jacobian[1][0].is_zero());
340 assert!(!jacobian[1][1].is_zero());
341 }
342
343 #[test]
344 fn test_single_variable_jacobian() {
345 let x = symbol!(x);
346
347 let functions = vec![Expression::pow(
348 Expression::symbol(x.clone()),
349 Expression::integer(2),
350 )];
351
352 let variables = vec![x.clone()];
353 let jacobian = JacobianOperations::compute(&functions, &variables);
354
355 assert_eq!(jacobian.len(), 1);
356 assert_eq!(jacobian[0].len(), 1);
357 assert!(!jacobian[0][0].is_zero());
358 }
359
360 #[test]
361 fn test_three_variable_jacobian() {
362 let x = symbol!(x);
363 let y = symbol!(y);
364 let z = symbol!(z);
365
366 let functions = vec![
367 Expression::add(vec![
368 Expression::symbol(x.clone()),
369 Expression::symbol(y.clone()),
370 Expression::symbol(z.clone()),
371 ]),
372 Expression::mul(vec![
373 Expression::symbol(x.clone()),
374 Expression::symbol(y.clone()),
375 ]),
376 Expression::pow(Expression::symbol(z.clone()), Expression::integer(2)),
377 ];
378
379 let variables = vec![x.clone(), y.clone(), z.clone()];
380 let jacobian = JacobianOperations::compute(&functions, &variables);
381
382 assert_eq!(jacobian.len(), 3);
383 for row in &jacobian {
384 assert_eq!(row.len(), 3);
385 }
386
387 assert_eq!(jacobian[0][0].simplify(), Expression::integer(1));
388 assert_eq!(jacobian[0][1].simplify(), Expression::integer(1));
389 assert_eq!(jacobian[0][2].simplify(), Expression::integer(1));
390
391 assert!(!jacobian[1][0].is_zero());
392 assert!(!jacobian[1][1].is_zero());
393 assert_eq!(jacobian[1][2].simplify(), Expression::integer(0));
394
395 assert_eq!(jacobian[2][0].simplify(), Expression::integer(0));
396 assert_eq!(jacobian[2][1].simplify(), Expression::integer(0));
397 assert!(!jacobian[2][2].is_zero());
398 }
399
400 #[test]
401 fn test_jacobian_caching() {
402 let x = symbol!(x);
403 let y = symbol!(y);
404
405 let functions = vec![
406 Expression::function("sin", vec![Expression::symbol(x.clone())]),
407 Expression::function("cos", vec![Expression::symbol(y.clone())]),
408 ];
409
410 let variables = vec![x.clone(), y.clone()];
411 let mut cache = HashMap::new();
412
413 let jacobian1 = JacobianOperations::compute_cached(&functions, &variables, &mut cache);
414 let jacobian2 = JacobianOperations::compute_cached(&functions, &variables, &mut cache);
415
416 assert_eq!(jacobian1.len(), 2);
417 assert_eq!(jacobian2.len(), 2);
418 assert_eq!(jacobian1[0][0], jacobian2[0][0]);
419 assert_eq!(jacobian1[1][1], jacobian2[1][1]);
420 assert_eq!(cache.len(), 4);
421 }
422
423 #[test]
424 fn test_jacobian_transpose() {
425 let x = symbol!(x);
426 let y = symbol!(y);
427
428 let functions = vec![
429 Expression::mul(vec![Expression::integer(2), Expression::symbol(x.clone())]),
430 Expression::mul(vec![Expression::integer(3), Expression::symbol(y.clone())]),
431 ];
432
433 let variables = vec![x.clone(), y.clone()];
434 let jacobian = JacobianOperations::compute(&functions, &variables);
435 let jacobian_t = JacobianOperations::transpose(&functions, &variables);
436
437 assert_eq!(jacobian.len(), 2);
438 assert_eq!(jacobian_t.len(), 2);
439 assert_eq!(jacobian[0][1], jacobian_t[1][0]);
440 assert_eq!(jacobian[1][0], jacobian_t[0][1]);
441 }
442
443 #[test]
444 fn test_jacobian_determinant_2x2() {
445 let x = symbol!(x);
446 let y = symbol!(y);
447
448 let functions = vec![
449 Expression::add(vec![
450 Expression::mul(vec![Expression::integer(2), Expression::symbol(x.clone())]),
451 Expression::symbol(y.clone()),
452 ]),
453 Expression::add(vec![
454 Expression::symbol(x.clone()),
455 Expression::mul(vec![Expression::integer(3), Expression::symbol(y.clone())]),
456 ]),
457 ];
458
459 let variables = vec![x.clone(), y.clone()];
460 let det = JacobianDeterminant::compute(&functions, &variables);
461
462 assert!(!det.is_zero());
463 }
464
465 #[test]
466 fn test_identity_transformation_jacobian() {
467 let x = symbol!(x);
468 let y = symbol!(y);
469
470 let functions = vec![Expression::symbol(x.clone()), Expression::symbol(y.clone())];
471
472 let variables = vec![x.clone(), y.clone()];
473 let jacobian = JacobianOperations::compute(&functions, &variables);
474
475 assert_eq!(jacobian[0][0].simplify(), Expression::integer(1));
476 assert_eq!(jacobian[0][1].simplify(), Expression::integer(0));
477 assert_eq!(jacobian[1][0].simplify(), Expression::integer(0));
478 assert_eq!(jacobian[1][1].simplify(), Expression::integer(1));
479
480 let det = JacobianDeterminant::compute(&functions, &variables);
481 assert_eq!(det.simplify(), Expression::integer(1));
482 }
483
484 #[test]
485 fn test_constant_functions_jacobian() {
486 let x = symbol!(x);
487 let y = symbol!(y);
488
489 let functions = vec![Expression::integer(5), Expression::integer(10)];
490
491 let variables = vec![x.clone(), y.clone()];
492 let jacobian = JacobianOperations::compute(&functions, &variables);
493
494 for row in &jacobian {
495 for elem in row {
496 assert_eq!(elem.simplify(), Expression::integer(0));
497 }
498 }
499
500 let det = JacobianDeterminant::compute(&functions, &variables);
501 assert_eq!(det.simplify(), Expression::integer(0));
502 }
503
504 #[test]
505 #[should_panic(expected = "Jacobian determinant requires square matrix")]
506 fn test_non_square_jacobian_determinant() {
507 let x = symbol!(x);
508 let y = symbol!(y);
509
510 let functions = vec![Expression::symbol(x.clone())];
511
512 let variables = vec![x, y];
513 JacobianDeterminant::compute(&functions, &variables);
514 }
515
516 #[test]
517 fn test_singular_jacobian() {
518 let x = symbol!(x);
519 let y = symbol!(y);
520
521 let functions = vec![Expression::symbol(x.clone()), Expression::symbol(x.clone())];
522
523 let variables = vec![x.clone(), y.clone()];
524 let is_singular = JacobianDeterminant::is_singular(&functions, &variables);
525
526 assert!(is_singular);
527 }
528
529 #[test]
530 fn test_jacobian_absolute_determinant() {
531 let x = symbol!(x);
532 let y = symbol!(y);
533
534 let functions = vec![
535 Expression::mul(vec![Expression::integer(-1), Expression::symbol(x.clone())]),
536 Expression::symbol(y.clone()),
537 ];
538
539 let variables = vec![x.clone(), y.clone()];
540 let abs_det = JacobianDeterminant::absolute(&functions, &variables);
541
542 assert!(!abs_det.is_zero());
543 }
544
545 #[test]
546 fn test_empty_jacobian() {
547 let functions: Vec<Expression> = vec![];
548 let variables: Vec<Symbol> = vec![];
549
550 let jacobian = JacobianOperations::compute(&functions, &variables);
551 assert_eq!(jacobian.len(), 0);
552 }
553
554 #[test]
555 fn test_rectangular_jacobian() {
556 let x = symbol!(x);
557 let y = symbol!(y);
558
559 let functions = vec![
560 Expression::symbol(x.clone()),
561 Expression::symbol(y.clone()),
562 Expression::add(vec![
563 Expression::symbol(x.clone()),
564 Expression::symbol(y.clone()),
565 ]),
566 ];
567
568 let variables = vec![x.clone(), y.clone()];
569 let jacobian = JacobianOperations::compute(&functions, &variables);
570
571 assert_eq!(jacobian.len(), 3);
572 for row in &jacobian {
573 assert_eq!(row.len(), 2);
574 }
575 }
576
577 #[test]
578 fn test_trigonometric_jacobian() {
579 let x = symbol!(x);
580 let y = symbol!(y);
581
582 let functions = vec![
583 Expression::function("sin", vec![Expression::symbol(x.clone())]),
584 Expression::function("cos", vec![Expression::symbol(y.clone())]),
585 ];
586
587 let variables = vec![x.clone(), y.clone()];
588 let jacobian = JacobianOperations::compute(&functions, &variables);
589
590 assert_eq!(jacobian.len(), 2);
591 assert!(!jacobian[0][0].is_zero());
592 assert_eq!(jacobian[0][1].simplify(), Expression::integer(0));
593 assert_eq!(jacobian[1][0].simplify(), Expression::integer(0));
594 assert!(!jacobian[1][1].is_zero());
595 }
596
597 #[test]
598 fn test_condition_number_estimate() {
599 let x = symbol!(x);
600 let y = symbol!(y);
601
602 let functions = vec![Expression::symbol(x.clone()), Expression::symbol(y.clone())];
603
604 let variables = vec![x.clone(), y.clone()];
605 let condition = JacobianDeterminant::condition_estimate(&functions, &variables);
606
607 assert!(!condition.is_zero());
608 }
609}