1#[cfg(test)]
2use crate::extension::LinalgOp;
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum LinalgAdRuleSupport {
16 Supported,
17 SupportedViaLinearize,
18 PartiallySupported,
19 NonDifferentiable,
20 Unsupported,
21 PendingOracle,
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35pub enum LinalgAdRoute {
36 Unsupported,
38 Linearize,
40 LinearizeThenTranspose,
43 LinearizeThenCustomLinearTranspose,
46 CustomVjp,
48 CustomPreferredWithLinearizeFallback,
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq)]
64pub struct LinalgAdModeSupport {
65 pub status: LinalgAdRuleSupport,
67 pub route: LinalgAdRoute,
69}
70
71#[derive(Clone, Copy, Debug, PartialEq, Eq)]
81pub enum LinalgAdOpKind {
82 Cholesky,
83 Lu,
84 LuFactor,
85 LuSolvePrepared,
86 FullPivLu,
87 FullPivLuSolve,
88 Svd,
89 SvdVals,
90 Qr,
91 Eigh,
92 EighVals,
93 Eig,
94 EigVals,
95 TriangularSolve,
96}
97
98impl LinalgAdOpKind {
99 pub const COUNT: usize = 14;
100
101 pub const fn as_index(self) -> usize {
111 match self {
112 Self::Cholesky => 0,
113 Self::Lu => 1,
114 Self::LuFactor => 2,
115 Self::LuSolvePrepared => 3,
116 Self::FullPivLu => 4,
117 Self::FullPivLuSolve => 5,
118 Self::Svd => 6,
119 Self::SvdVals => 7,
120 Self::Qr => 8,
121 Self::Eigh => 9,
122 Self::EighVals => 10,
123 Self::Eig => 11,
124 Self::EigVals => 12,
125 Self::TriangularSolve => 13,
126 }
127 }
128
129 #[cfg(test)]
130 pub(crate) const fn from_linalg_op(op: LinalgOp) -> Self {
131 match op {
132 LinalgOp::Cholesky => Self::Cholesky,
133 LinalgOp::Lu => Self::Lu,
134 LinalgOp::LuFactor => Self::LuFactor,
135 LinalgOp::LuSolvePrepared { .. } => Self::LuSolvePrepared,
136 LinalgOp::FullPivLu => Self::FullPivLu,
137 LinalgOp::FullPivLuSolve { .. } => Self::FullPivLuSolve,
138 LinalgOp::Svd { .. } => Self::Svd,
139 LinalgOp::SvdVals { .. } => Self::SvdVals,
140 LinalgOp::Qr { .. } => Self::Qr,
141 LinalgOp::Eigh { .. } => Self::Eigh,
142 LinalgOp::EighVals { .. } => Self::EighVals,
143 LinalgOp::Eig { .. } => Self::Eig,
144 LinalgOp::EigVals { .. } => Self::EigVals,
145 LinalgOp::TriangularSolve { .. } => Self::TriangularSolve,
146 }
147 }
148}
149
150#[derive(Clone, Copy, Debug, PartialEq, Eq)]
162pub struct LinalgAdOutputSupport {
163 pub index: usize,
165 pub name: &'static str,
167 pub status: LinalgAdRuleSupport,
169}
170
171#[derive(Clone, Copy, Debug, PartialEq, Eq)]
182pub struct LinalgAdSupport {
183 pub kind: LinalgAdOpKind,
185 pub jvp: LinalgAdModeSupport,
187 pub vjp: LinalgAdModeSupport,
189 pub linearize_rule: LinalgAdRuleSupport,
191 pub custom_vjp_rule: LinalgAdRuleSupport,
193 pub custom_linear_transpose_rule: LinalgAdRuleSupport,
195 pub linearize: LinalgAdRuleSupport,
197 pub transpose: LinalgAdRuleSupport,
199 pub outputs: &'static [LinalgAdOutputSupport],
201 pub caveats: &'static [&'static str],
203}
204
205const fn mode(status: LinalgAdRuleSupport, route: LinalgAdRoute) -> LinalgAdModeSupport {
206 LinalgAdModeSupport { status, route }
207}
208
209const fn jvp_route(status: LinalgAdRuleSupport) -> LinalgAdRoute {
210 match status {
211 LinalgAdRuleSupport::Unsupported
212 | LinalgAdRuleSupport::NonDifferentiable
213 | LinalgAdRuleSupport::PendingOracle => LinalgAdRoute::Unsupported,
214 LinalgAdRuleSupport::Supported
215 | LinalgAdRuleSupport::SupportedViaLinearize
216 | LinalgAdRuleSupport::PartiallySupported => LinalgAdRoute::Linearize,
217 }
218}
219
220const fn support_entry(
221 kind: LinalgAdOpKind,
222 linearize: LinalgAdRuleSupport,
223 transpose: LinalgAdRuleSupport,
224 vjp: LinalgAdModeSupport,
225 custom_linear_transpose_rule: LinalgAdRuleSupport,
226 outputs: &'static [LinalgAdOutputSupport],
227 caveats: &'static [&'static str],
228) -> LinalgAdSupport {
229 LinalgAdSupport {
230 kind,
231 jvp: mode(linearize, jvp_route(linearize)),
232 vjp,
233 linearize_rule: linearize,
234 custom_vjp_rule: LinalgAdRuleSupport::Unsupported,
235 custom_linear_transpose_rule,
236 linearize,
237 transpose,
238 outputs,
239 caveats,
240 }
241}
242
243const fn output(
244 index: usize,
245 name: &'static str,
246 status: LinalgAdRuleSupport,
247) -> LinalgAdOutputSupport {
248 LinalgAdOutputSupport {
249 index,
250 name,
251 status,
252 }
253}
254
255static CHOLESKY_OUTPUTS: [LinalgAdOutputSupport; 1] = [output(
256 0,
257 "factor",
258 LinalgAdRuleSupport::SupportedViaLinearize,
259)];
260static LU_OUTPUTS: [LinalgAdOutputSupport; 4] = [
261 output(0, "p", LinalgAdRuleSupport::NonDifferentiable),
262 output(1, "l", LinalgAdRuleSupport::SupportedViaLinearize),
263 output(2, "u", LinalgAdRuleSupport::SupportedViaLinearize),
264 output(3, "parity", LinalgAdRuleSupport::NonDifferentiable),
265];
266static LU_FACTOR_OUTPUTS: [LinalgAdOutputSupport; 3] = [
267 output(0, "packed_lu", LinalgAdRuleSupport::Unsupported),
268 output(1, "pivots", LinalgAdRuleSupport::NonDifferentiable),
269 output(2, "parity", LinalgAdRuleSupport::NonDifferentiable),
270];
271static SOLUTION_OUTPUTS: [LinalgAdOutputSupport; 1] = [output(
272 0,
273 "solution",
274 LinalgAdRuleSupport::SupportedViaLinearize,
275)];
276static FULL_PIV_LU_OUTPUTS: [LinalgAdOutputSupport; 5] = [
277 output(0, "p", LinalgAdRuleSupport::NonDifferentiable),
278 output(1, "l", LinalgAdRuleSupport::SupportedViaLinearize),
279 output(2, "u", LinalgAdRuleSupport::SupportedViaLinearize),
280 output(3, "q", LinalgAdRuleSupport::NonDifferentiable),
281 output(4, "parity", LinalgAdRuleSupport::NonDifferentiable),
282];
283static FULL_PIV_LU_SOLVE_OUTPUTS: [LinalgAdOutputSupport; 1] = [output(
284 0,
285 "solution",
286 LinalgAdRuleSupport::SupportedViaLinearize,
287)];
288static SVD_OUTPUTS: [LinalgAdOutputSupport; 3] = [
289 output(0, "u", LinalgAdRuleSupport::SupportedViaLinearize),
290 output(
291 1,
292 "singular_values",
293 LinalgAdRuleSupport::SupportedViaLinearize,
294 ),
295 output(2, "vt", LinalgAdRuleSupport::SupportedViaLinearize),
296];
297static SVD_VALS_OUTPUTS: [LinalgAdOutputSupport; 1] = [output(
298 0,
299 "singular_values",
300 LinalgAdRuleSupport::SupportedViaLinearize,
301)];
302static QR_OUTPUTS: [LinalgAdOutputSupport; 2] = [
303 output(0, "q", LinalgAdRuleSupport::SupportedViaLinearize),
304 output(1, "r", LinalgAdRuleSupport::SupportedViaLinearize),
305];
306static EIGH_OUTPUTS: [LinalgAdOutputSupport; 2] = [
307 output(0, "eigenvalues", LinalgAdRuleSupport::SupportedViaLinearize),
308 output(
309 1,
310 "eigenvectors",
311 LinalgAdRuleSupport::SupportedViaLinearize,
312 ),
313];
314static EIGH_VALS_OUTPUTS: [LinalgAdOutputSupport; 1] = [output(
315 0,
316 "eigenvalues",
317 LinalgAdRuleSupport::SupportedViaLinearize,
318)];
319static EIG_OUTPUTS: [LinalgAdOutputSupport; 2] = [
320 output(0, "eigenvalues", LinalgAdRuleSupport::SupportedViaLinearize),
321 output(1, "eigenvectors", LinalgAdRuleSupport::Unsupported),
322];
323static EIG_VALS_OUTPUTS: [LinalgAdOutputSupport; 1] = [output(
324 0,
325 "eigenvalues",
326 LinalgAdRuleSupport::SupportedViaLinearize,
327)];
328
329static DECOMPOSITION_CAVEATS: [&str; 1] = [
330 "Derivative regularization handles near-degenerate spectra but does not make exact degeneracies smoothly differentiable.",
331];
332
333static LINALG_AD_SUPPORT: [LinalgAdSupport; LinalgAdOpKind::COUNT] = [
334 support_entry(
335 LinalgAdOpKind::Cholesky,
336 LinalgAdRuleSupport::SupportedViaLinearize,
337 LinalgAdRuleSupport::Unsupported,
338 mode(
339 LinalgAdRuleSupport::SupportedViaLinearize,
340 LinalgAdRoute::LinearizeThenTranspose,
341 ),
342 LinalgAdRuleSupport::Unsupported,
343 &CHOLESKY_OUTPUTS,
344 &DECOMPOSITION_CAVEATS,
345 ),
346 support_entry(
347 LinalgAdOpKind::Lu,
348 LinalgAdRuleSupport::PartiallySupported,
349 LinalgAdRuleSupport::Unsupported,
350 mode(
351 LinalgAdRuleSupport::PartiallySupported,
352 LinalgAdRoute::LinearizeThenTranspose,
353 ),
354 LinalgAdRuleSupport::Unsupported,
355 &LU_OUTPUTS,
356 &DECOMPOSITION_CAVEATS,
357 ),
358 support_entry(
359 LinalgAdOpKind::LuFactor,
360 LinalgAdRuleSupport::Unsupported,
361 LinalgAdRuleSupport::Unsupported,
362 mode(LinalgAdRuleSupport::Unsupported, LinalgAdRoute::Unsupported),
363 LinalgAdRuleSupport::Unsupported,
364 &LU_FACTOR_OUTPUTS,
365 &[],
366 ),
367 support_entry(
368 LinalgAdOpKind::LuSolvePrepared,
369 LinalgAdRuleSupport::SupportedViaLinearize,
370 LinalgAdRuleSupport::PartiallySupported,
371 mode(
372 LinalgAdRuleSupport::SupportedViaLinearize,
373 LinalgAdRoute::LinearizeThenCustomLinearTranspose,
374 ),
375 LinalgAdRuleSupport::PartiallySupported,
376 &SOLUTION_OUTPUTS,
377 &[],
378 ),
379 support_entry(
380 LinalgAdOpKind::FullPivLu,
381 LinalgAdRuleSupport::SupportedViaLinearize,
382 LinalgAdRuleSupport::Unsupported,
383 mode(
384 LinalgAdRuleSupport::SupportedViaLinearize,
385 LinalgAdRoute::LinearizeThenTranspose,
386 ),
387 LinalgAdRuleSupport::Unsupported,
388 &FULL_PIV_LU_OUTPUTS,
389 &DECOMPOSITION_CAVEATS,
390 ),
391 support_entry(
392 LinalgAdOpKind::FullPivLuSolve,
393 LinalgAdRuleSupport::SupportedViaLinearize,
394 LinalgAdRuleSupport::Supported,
395 mode(
396 LinalgAdRuleSupport::SupportedViaLinearize,
397 LinalgAdRoute::LinearizeThenCustomLinearTranspose,
398 ),
399 LinalgAdRuleSupport::Supported,
400 &FULL_PIV_LU_SOLVE_OUTPUTS,
401 &[],
402 ),
403 support_entry(
404 LinalgAdOpKind::Svd,
405 LinalgAdRuleSupport::SupportedViaLinearize,
406 LinalgAdRuleSupport::Unsupported,
407 mode(
408 LinalgAdRuleSupport::SupportedViaLinearize,
409 LinalgAdRoute::LinearizeThenTranspose,
410 ),
411 LinalgAdRuleSupport::Unsupported,
412 &SVD_OUTPUTS,
413 &DECOMPOSITION_CAVEATS,
414 ),
415 support_entry(
416 LinalgAdOpKind::SvdVals,
417 LinalgAdRuleSupport::SupportedViaLinearize,
418 LinalgAdRuleSupport::Unsupported,
419 mode(
420 LinalgAdRuleSupport::SupportedViaLinearize,
421 LinalgAdRoute::LinearizeThenTranspose,
422 ),
423 LinalgAdRuleSupport::Unsupported,
424 &SVD_VALS_OUTPUTS,
425 &DECOMPOSITION_CAVEATS,
426 ),
427 support_entry(
428 LinalgAdOpKind::Qr,
429 LinalgAdRuleSupport::SupportedViaLinearize,
430 LinalgAdRuleSupport::Unsupported,
431 mode(
432 LinalgAdRuleSupport::SupportedViaLinearize,
433 LinalgAdRoute::LinearizeThenTranspose,
434 ),
435 LinalgAdRuleSupport::Unsupported,
436 &QR_OUTPUTS,
437 &DECOMPOSITION_CAVEATS,
438 ),
439 support_entry(
440 LinalgAdOpKind::Eigh,
441 LinalgAdRuleSupport::SupportedViaLinearize,
442 LinalgAdRuleSupport::Unsupported,
443 mode(
444 LinalgAdRuleSupport::SupportedViaLinearize,
445 LinalgAdRoute::LinearizeThenTranspose,
446 ),
447 LinalgAdRuleSupport::Unsupported,
448 &EIGH_OUTPUTS,
449 &DECOMPOSITION_CAVEATS,
450 ),
451 support_entry(
452 LinalgAdOpKind::EighVals,
453 LinalgAdRuleSupport::SupportedViaLinearize,
454 LinalgAdRuleSupport::Unsupported,
455 mode(
456 LinalgAdRuleSupport::SupportedViaLinearize,
457 LinalgAdRoute::LinearizeThenTranspose,
458 ),
459 LinalgAdRuleSupport::Unsupported,
460 &EIGH_VALS_OUTPUTS,
461 &DECOMPOSITION_CAVEATS,
462 ),
463 support_entry(
464 LinalgAdOpKind::Eig,
465 LinalgAdRuleSupport::PartiallySupported,
466 LinalgAdRuleSupport::Unsupported,
467 mode(
468 LinalgAdRuleSupport::PartiallySupported,
469 LinalgAdRoute::LinearizeThenTranspose,
470 ),
471 LinalgAdRuleSupport::Unsupported,
472 &EIG_OUTPUTS,
473 &DECOMPOSITION_CAVEATS,
474 ),
475 support_entry(
476 LinalgAdOpKind::EigVals,
477 LinalgAdRuleSupport::SupportedViaLinearize,
478 LinalgAdRuleSupport::Unsupported,
479 mode(
480 LinalgAdRuleSupport::SupportedViaLinearize,
481 LinalgAdRoute::LinearizeThenTranspose,
482 ),
483 LinalgAdRuleSupport::Unsupported,
484 &EIG_VALS_OUTPUTS,
485 &DECOMPOSITION_CAVEATS,
486 ),
487 support_entry(
488 LinalgAdOpKind::TriangularSolve,
489 LinalgAdRuleSupport::SupportedViaLinearize,
490 LinalgAdRuleSupport::Supported,
491 mode(
492 LinalgAdRuleSupport::SupportedViaLinearize,
493 LinalgAdRoute::LinearizeThenCustomLinearTranspose,
494 ),
495 LinalgAdRuleSupport::Supported,
496 &SOLUTION_OUTPUTS,
497 &[],
498 ),
499];
500
501pub fn all_linalg_ad_support() -> &'static [LinalgAdSupport; LinalgAdOpKind::COUNT] {
510 &LINALG_AD_SUPPORT
511}
512
513pub fn linalg_ad_support(kind: LinalgAdOpKind) -> &'static LinalgAdSupport {
524 &LINALG_AD_SUPPORT[kind.as_index()]
525}
526
527#[cfg(test)]
528pub(crate) fn linalg_ad_support_for_op(op: LinalgOp) -> &'static LinalgAdSupport {
529 linalg_ad_support(LinalgAdOpKind::from_linalg_op(op))
530}
531
532#[cfg(test)]
533mod tests;