Skip to content

Giles method to differentiate solvers and minor edits to docs - #11

Open
Shreyas911 wants to merge 3 commits into
Reference-LAPACK:mainfrom
Shreyas911:main
Open

Giles method to differentiate solvers and minor edits to docs#11
Shreyas911 wants to merge 3 commits into
Reference-LAPACK:mainfrom
Shreyas911:main

Conversation

@Shreyas911

@Shreyas911 Shreyas911 commented Jul 26, 2026

Copy link
Copy Markdown

What

Two things in this PR:

  1. README fix: a standard CMake build of Reference LAPACK produces libblas.a (and liblapack.a), not librefblas.a, but both the Make and Meson build paths in this repo expect librefblas.a by default (-lrefblas / -Dlibblas=refblas). Following the README as written with a CMake-built LAPACK fails at link time with no real explanation of why.
  2. DTRSM differentiation using the Giles method: adds hand-written forward, reverse, vector-forward, and vector-reverse derivatives of DTRSM (dtrsm_d.f, dtrsm_b.f, dtrsm_dv.f, dtrsm_bv.f). Instead of differentiating the triangular substitution algorithm line by line like Tapenade does, these differentiate the defining relation op(A)*X = alpha*B directly, so the derivative is expressed purely as calls to the original DTRSM and DTRMM. This is the "black box" adjoint approach from Christianson (1994) and Giles (2008), extended here from the vector-solve case in those papers to the BLAS3 matrix case.

Fix

README (docs only): Documents two ways to handle the naming mismatch on the Make path (symlink libblas.a to librefblas.a, or override BLAS_LIB with the full linker flags) plus the equivalent Meson option (-Dlibblas=blas). Also updates the make line to make # use make -j 8 or make -j 16 for a faster build using multi-threading, so readers know they can parallelize the build.

DTRSM (new source):

  • dtrsm_d.f: forward mode. Comes from perturbing op(A)*X = alpha*B. Works out to one DTRMM call followed by one DTRSM call.
  • dtrsm_b.f: reverse mode. The adjoint comes from solving the transposed system with the seed as the right-hand side, then a rank-restricted outer product for Ab (only touching the triangle DTRSM actually reads, and zeroing the diagonal when DIAG='U').
  • dtrsm_dv.f / dtrsm_bv.f: vector versions of the above. Each direction is computed independently (forward mode is linear in the seed direction, and each reverse-mode direction is its own adjoint solve), though the shared primal solution X is only computed once, outside the per-direction loop. Neither one calls the scalar _d/_b routines. Tapenade's layout stores the direction as the fastest-varying array index (nbdirsmax, LDA/LDB, *), which isn't something you can hand straight to a BLAS call, so each routine gathers a direction's data into a plain contiguous array first, calls DTRSM/DTRMM, then scatters the result back.

Also fixes a bug that was already in the auto-generated test_dtrsm.f90 and test_dtrsm_reverse.f90 (generated by run_tapenade_blas.py, not by Tapenade itself): both declared a random-seed array twice, with the second declaration coming after executable statements, which Fortran doesn't allow. That's a compile failure unrelated to anything in this PR. (test_dtrsm_vector_forward.f90 and test_dtrsm_vector_reverse.f90 didn't have this problem.)

Testing

README / library naming, checked against a CMake-built Reference LAPACK:

  • ln -s libblas.a librefblas.a + make
  • make BLAS_LIB="-L$LAPACKDIR -lblas"
  • meson setup builddir -Dlibblas=blas -Dlibblas_path=$LAPACKDIR && meson compile -C builddir

DTRSM Giles-method routines, checked with finite-difference/VJP tests in all four generated test programs:

  • test_dtrsm (forward mode): PASS
  • test_dtrsm_reverse (reverse mode): PASS
  • test_dtrsm_vector_forward (vector forward, nbdirs=4): PASS
  • test_dtrsm_vector_reverse (vector reverse, nbdirs=4): PASS

Notes for reviewers

  • These DTRSM routines are hand-written, not Tapenade output, and they're meant to replace whatever run_tapenade_blas.py would otherwise generate for this routine. The substitution-loop version works fine; it just carries a checkpointing cost (a PUSHREAL8/POPREAL8 stack) that the black-box version avoids completely. Happy to discuss whether the generator should skip DTRSM if it gets re-run later.
  • Separately, I noticed run_tapenade_blas.py rewrites top-level build files (Makefile, meson.build, run_tests.sh, the Python interface test files) every time it runs, regardless of --file/--mode. Can open that as its own issue if that's useful.

Reference LAPACK's CMake build produces libblas.a/liblapack.a rather
than librefblas.a, which the Make and Meson builds expect by default.
Document the symlink/override workarounds.

Tested (Make path only; Meson not installed locally):
- Make + symlink (ln -s libblas.a librefblas.a): build succeeds
- Make + BLAS_LIB override (BLAS_LIB="-L$LAPACKDIR -lblas"): build succeeds
- Meson + -Dlibblas=blas -Dlibblas_path=$LAPACKDIR: build succeeds
@Shreyas911 Shreyas911 changed the title docs: clarify BLAS library naming for CMake-built LAPACK Giles method to differentiate solvers and minor edits to docs Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant