Skip to content

Conversation

ilayn
Copy link
Member

@ilayn ilayn commented Aug 4, 2024

Part of #18566

This one has a very annoying todo list, so I'll leave it here for folks who know about these issues.

  • Compilation
    • Figure out whether to compile as static + Cython bridge or create a module directly in the header file ala __quadpack.h/__minpack.h. (Both are fine, just as a todo item)
    • Include LAPACK in the header file (it needs daxpy, ddot, dcopy, dscal, dnrm2, dtrtrs, dpotrf)
    • Distinguish ILP64
  • Convert all dummy printing to Python logging module (for example https://gist.github.com/hensing/0db3f8e3a99590006368)
  • Decide on keeping timing information (does anyone use or trust those numbers is it better with time.h of C)?

@ilayn ilayn added enhancement A new feature or improvement scipy.optimize maintenance Items related to regular maintenance tasks C/C++ Items related to the internal C/C++ code base Meson Items related to the introduction of Meson as the new build system for SciPy labels Aug 4, 2024
@ilayn ilayn added this to the 1.15.0 milestone Aug 4, 2024
@ilayn ilayn requested a review from andyfaff as a code owner August 4, 2024 09:10
@github-actions github-actions bot added the Fortran Items related to the internal Fortran code base label Aug 4, 2024
@ilayn ilayn marked this pull request as draft August 4, 2024 09:10
@ilayn ilayn mentioned this pull request Aug 4, 2024
37 tasks
#include <math.h>

void
setulb(const int,const int,double*,double*,double*,int*,double*,double*,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add documentation onto what each parameter represents, and their required sizes? this info can be adapted from the Fortran.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes typically I do that in the last commit to have the last shape. This is just to get the compilation mechanism to put in place.

@andyfaff
Copy link
Contributor

andyfaff commented Aug 4, 2024

IMO we would probably lose all the printing/timing info.

@ev-br
Copy link
Member

ev-br commented Aug 4, 2024

LAPACK should be similar to gh-19970, modulo ILP64 which is not available yet anyhow.
Also if you're lucky, BLAS prototypes are already in npy_cblas.h

@ilayn
Copy link
Member Author

ilayn commented Aug 4, 2024

IMO we would probably lose all the printing/timing info.

I have the same opinion but no idea how this is used in the wild. However, I got rid of text based branching anyways so we can immediately remove the printing if we are OK with it.

LAPACK should be similar to #19970, modulo ILP64 which is not available yet anyhow.
Also if you're lucky, BLAS prototypes are already in npy_cblas.h

Let me try; fingers crossed.

@rgommers
Copy link
Member

rgommers commented Aug 5, 2024

+1 for removing printing and timing all at once. No one should be relying on the timer calls, that kind of thing is just in the code we vendored way back when.

@ilayn
Copy link
Member Author

ilayn commented Aug 6, 2024

@ev-br I've restructured things a bit and removed the timing and the printing stuff. Now only errors I'm getting is the BLAS and LAPACK functions not being found.

Could you please take a look at the lapack importing parts and let me know how to compile? There are still bugs to clean up but I'll start doing that after I can start compiling.

The ones that I need are at the top of the header file.

@ev-br
Copy link
Member

ev-br commented Aug 6, 2024

A short partial answer for now only, sorry. In short, you're missing function prototypes.
You can get a bit further by

$ git diff
diff --git a/scipy/optimize/__lbfgsb.h b/scipy/optimize/__lbfgsb.h
index 3912c91a99..cd9cdc7f66 100644
--- a/scipy/optimize/__lbfgsb.h
+++ b/scipy/optimize/__lbfgsb.h
@@ -11,10 +11,17 @@ extern "C"
 #include <math.h>
 
 #define PYERR(errobj,message) {PyErr_SetString(errobj,message); return NULL;}
-#define DNRM2 BLAS_FUNC(dnrm2)
-#define DDOT BLAS_FUNC(ddot)
-#define DCOPY BLAS_FUNC(dcopy)
-#define DTRSM BLAS_FUNC(dtrsm)
+
+// these are declared in ../_build_utils/npy_cblas_base.h via npy_cblas.h
+# define DNRM2 cblas_dnrm2
+# define DCOPY cblas_dcopy
+# define DDOT cblas_ddot
+# define DTRSM cblas_dtrsm
+
+//#define DDOT BLAS_FUNC(ddot)
+//#define DCOPY BLAS_FUNC(dcopy)
+//#define DNRM2 BLAS_FUNC(dnrm2)
+//#define DTRSM BLAS_FUNC(dtrsm)

but these are CBLAS symbols (whatever that means) and you're using the original Fortran symbols. Maybe you can restructure your usage to use CBLAS (and then it should be ILP64-proof?) or stick with `BLAS_FUNC and add prototypes like in https://github.com/scipy/scipy/pull/19970/files#diff-1d06e2703e983014d899d2c42544507f537a5c6eebc84ec484cf125eed03e102R83

You'll need this anyway for potrf since npy_cblas.h does not have its prototype.

One other thing is that in the meson.build you're declaring the dependency on np_dep, blas, lapack. Elsewhere I saw weird compile/link errors until adding also py3_dep (cf https://github.com/scipy/scipy/pull/19970/files#diff-85ab739d8d3eb989d275834cd1da1d3302bedb33b8ecbef8656831d6ad026edbR126). No idea of why and how, just noting that it did work once :-).

@ilayn
Copy link
Member Author

ilayn commented Aug 6, 2024

CBLAS is also fine, they have the same signature but no dice yet. I used the following

# define DNRM2 cblas_dnrm2
# define DCOPY cblas_dcopy
# define DDOT cblas_ddot
# define DAXPY cblas_daxpy
 dependencies: [np_dep, blas, lapack, py3_dep],
 .. or 
 dependencies: [np_dep, blas_dep, lapack_dep, py3_dep],

And I get, mentioning the scipy_ prefixed ones,

In file included from ../scipy/optimize/__lbfgsb.c:1:
../scipy/optimize/__lbfgsb.c: In function 'mainlb':
../scipy/optimize/__lbfgsb.h:15:16: error: implicit declaration of function 'cblas_dcopy'; did you mean 'scipy_cblas_dcopy'? [-Wimplicit-function-declaration]
   15 | # define DCOPY cblas_dcopy
      |                ^~~~~~~~~~~
../scipy/optimize/__lbfgsb.c:324:17: note: in expansion of macro 'DCOPY'
  324 |                 DCOPY(&n, t, &one_int, x, &one_int);
      |                 ^~~~~
../scipy/optimize/__lbfgsb.h:14:16: error: implicit declaration of function 'cblas_dnrm2' [-Wimplicit-function-declaration]
   14 | # define DNRM2 cblas_dnrm2
      |                ^~~~~~~~~~~
../scipy/optimize/__lbfgsb.c:555:14: note: in expansion of macro 'DNRM2'
  555 |     rr = pow(DNRM2(&n, r, &one_int), 2.0);
      |              ^~~~~
../scipy/optimize/__lbfgsb.c:563:9: error: implicit declaration of function 'dscal'; did you mean '_scalb'? [-Wimplicit-function-declaration]
  563 |         dscal(&n, &stp, d, &one_int);
      |         ^~~~~
      |         _scalb
../scipy/optimize/__lbfgsb.c: In function 'bmv':
../scipy/optimize/__lbfgsb.c:757:5: error: implicit declaration of function 'DTRTRS' [-Wimplicit-function-declaration]
  757 |     DTRTRS("U", "T", "N", &col, &one_int, wt, &m, &p[col], &col, info);
      |     ^~~~~~
../scipy/optimize/__lbfgsb.c: In function 'cauchy':
../scipy/optimize/__lbfgsb.h:16:15: error: implicit declaration of function 'cblas_ddot'; did you mean 'scipy_cblas_ddot'? [-Wimplicit-function-declaration]
   16 | # define DDOT cblas_ddot
      |               ^~~~~~~~~~
../scipy/optimize/__lbfgsb.c:931:19: note: in expansion of macro 'DDOT'
  931 |         f2 = f2 - DDOT(&col2, v, &one_int, p, &one_int);
      |                   ^~~~
../scipy/optimize/__lbfgsb.c:1011:13: error: implicit declaration of function 'daxpy' [-Wimplicit-function-declaration]
 1011 |             daxpy(&col2, &dt, p, &one_int, c, &one_int);
      |             ^~~~~
../scipy/optimize/__lbfgsb.c: In function 'formk':
../scipy/optimize/__lbfgsb.c:1367:5: error: implicit declaration of function 'DPOTRF' [-Wimplicit-function-declaration]
 1367 |     DPOTRF('U', &col, wn, &m2, info);
      |     ^~~~~~
../scipy/optimize/__lbfgsb.c: In function 'lnsrlb':
../scipy/optimize/__lbfgsb.c:1590:14: error: implicit declaration of function 'dnrm2' [-Wimplicit-function-declaration]
 1590 |     *dnorm = dnrm2(&n, d, &one_int);
      |              ^~~~~
[28/29] Module scanner.
ninja: build stopped: subcommand failed.
Build failed!

No rush though. I'll keep trying different stuff. I am almost certain that what we are doing is something fundamentally cumbersome and there must be a better way for this. I just don't know what yet.

@ev-br
Copy link
Member

ev-br commented Aug 6, 2024

Okkkay, I see something different:

$ python dev.py build
💻  ninja -C /home/br/repos/scipy/scipy/build -j4
ninja: Entering directory `/home/br/repos/scipy/scipy/build'
[3/4] Compiling C object scipy/optimize/_lbfgsb.cpython-312-x86_64-linux-gnu.so.p/__lbfgsb.c.o
FAILED: scipy/optimize/_lbfgsb.cpython-312-x86_64-linux-gnu.so.p/__lbfgsb.c.o 
/home/br/miniforge3/envs/scipy-dev/bin/x86_64-conda-linux-gnu-cc -Iscipy/optimize/_lbfgsb.cpython-312-x86_64-linux-gnu.so.p -Iscipy/optimize -I../scipy/optimize -I../../../../miniforge3/envs/scipy-dev/lib/python3.12/site-packages/numpy/core/include -I/home/br/miniforge3/envs/scipy-dev/include -I/home/br/miniforge3/envs/scipy-dev/include/python3.12 -fvisibility=hidden -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -std=c17 -O2 -g -Wno-unused-but-set-variable -Wno-unused-function -Wno-conversion -Wno-misleading-indentation -march=nocona -mtune=haswell -ftree-vectorize -fPIC -fstack-protector-strong -fno-plt -O2 -ffunction-sections -pipe -isystem /home/br/miniforge3/envs/scipy-dev/include -DNDEBUG -D_FORTIFY_SOURCE=2 -O2 -isystem /home/br/miniforge3/envs/scipy-dev/include -fPIC -DNPY_NO_DEPRECATED_API=NPY_1_9_API_VERSION -MD -MQ scipy/optimize/_lbfgsb.cpython-312-x86_64-linux-gnu.so.p/__lbfgsb.c.o -MF scipy/optimize/_lbfgsb.cpython-312-x86_64-linux-gnu.so.p/__lbfgsb.c.o.d -o scipy/optimize/_lbfgsb.cpython-312-x86_64-linux-gnu.so.p/__lbfgsb.c.o -c ../scipy/optimize/__lbfgsb.c
../scipy/optimize/__lbfgsb.c: In function 'mainlb':
../scipy/optimize/__lbfgsb.c:324:23: warning: passing argument 1 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  324 |                 DCOPY(&n, t, &one_int, x, &one_int);
      |                       ^~
      |                       |
      |                       const int *
In file included from ../scipy/optimize/../_build_utils/src/npy_cblas.h:76,
                 from ../scipy/optimize/__lbfgsb.h:9,
                 from ../scipy/optimize/__lbfgsb.c:1:
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:80:42: note: expected 'int' but argument is of type 'const int *'
   80 | void BLASNAME(cblas_dcopy)(const BLASINT N, const double *X, const BLASINT incX,
      |                            ~~~~~~~~~~~~~~^
../scipy/optimize/__lbfgsb.c:324:30: warning: passing argument 3 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  324 |                 DCOPY(&n, t, &one_int, x, &one_int);
      |                              ^~~~~~~~
      |                              |
      |                              int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:80:76: note: expected 'int' but argument is of type 'int *'
   80 | void BLASNAME(cblas_dcopy)(const BLASINT N, const double *X, const BLASINT incX,
      |                                                              ~~~~~~~~~~~~~~^~~~
../scipy/optimize/__lbfgsb.c:324:43: warning: passing argument 5 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  324 |                 DCOPY(&n, t, &one_int, x, &one_int);
      |                                           ^~~~~~~~
      |                                           |
      |                                           int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:81:53: note: expected 'int' but argument is of type 'int *'
   81 |                            double *Y, const BLASINT incY);
      |                                       ~~~~~~~~~~~~~~^~~~
../scipy/optimize/__lbfgsb.c:325:23: warning: passing argument 1 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  325 |                 DCOPY(&n, r, &one_int, g, &one_int);
      |                       ^~
      |                       |
      |                       const int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:80:42: note: expected 'int' but argument is of type 'const int *'
   80 | void BLASNAME(cblas_dcopy)(const BLASINT N, const double *X, const BLASINT incX,
      |                            ~~~~~~~~~~~~~~^
../scipy/optimize/__lbfgsb.c:325:30: warning: passing argument 3 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  325 |                 DCOPY(&n, r, &one_int, g, &one_int);
      |                              ^~~~~~~~
      |                              |
      |                              int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:80:76: note: expected 'int' but argument is of type 'int *'
   80 | void BLASNAME(cblas_dcopy)(const BLASINT N, const double *X, const BLASINT incX,
      |                                                              ~~~~~~~~~~~~~~^~~~
../scipy/optimize/__lbfgsb.c:325:43: warning: passing argument 5 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  325 |                 DCOPY(&n, r, &one_int, g, &one_int);
      |                                           ^~~~~~~~
      |                                           |
      |                                           int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:81:53: note: expected 'int' but argument is of type 'int *'
   81 |                            double *Y, const BLASINT incY);
      |                                       ~~~~~~~~~~~~~~^~~~
../scipy/optimize/__lbfgsb.c:358:15: warning: passing argument 1 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  358 |         DCOPY(&n, x, &one_int, z, &one_int);
      |               ^~
      |               |
      |               const int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:80:42: note: expected 'int' but argument is of type 'const int *'
   80 | void BLASNAME(cblas_dcopy)(const BLASINT N, const double *X, const BLASINT incX,
      |                            ~~~~~~~~~~~~~~^
../scipy/optimize/__lbfgsb.c:358:22: warning: passing argument 3 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  358 |         DCOPY(&n, x, &one_int, z, &one_int);
      |                      ^~~~~~~~
      |                      |
      |                      int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:80:76: note: expected 'int' but argument is of type 'int *'
   80 | void BLASNAME(cblas_dcopy)(const BLASINT N, const double *X, const BLASINT incX,
      |                                                              ~~~~~~~~~~~~~~^~~~
../scipy/optimize/__lbfgsb.c:358:35: warning: passing argument 5 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  358 |         DCOPY(&n, x, &one_int, z, &one_int);
      |                                   ^~~~~~~~
      |                                   |
      |                                   int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:81:53: note: expected 'int' but argument is of type 'int *'
   81 |                            double *Y, const BLASINT incY);
      |                                       ~~~~~~~~~~~~~~^~~~
../scipy/optimize/__lbfgsb.c:469:15: warning: passing argument 1 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  469 |         DCOPY(&n, t, &one_int, x, &one_int);
      |               ^~
      |               |
      |               const int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:80:42: note: expected 'int' but argument is of type 'const int *'
   80 | void BLASNAME(cblas_dcopy)(const BLASINT N, const double *X, const BLASINT incX,
      |                            ~~~~~~~~~~~~~~^
../scipy/optimize/__lbfgsb.c:469:22: warning: passing argument 3 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  469 |         DCOPY(&n, t, &one_int, x, &one_int);
      |                      ^~~~~~~~
      |                      |
      |                      int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:80:76: note: expected 'int' but argument is of type 'int *'
   80 | void BLASNAME(cblas_dcopy)(const BLASINT N, const double *X, const BLASINT incX,
      |                                                              ~~~~~~~~~~~~~~^~~~
../scipy/optimize/__lbfgsb.c:469:35: warning: passing argument 5 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  469 |         DCOPY(&n, t, &one_int, x, &one_int);
      |                                   ^~~~~~~~
      |                                   |
      |                                   int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:81:53: note: expected 'int' but argument is of type 'int *'
   81 |                            double *Y, const BLASINT incY);
      |                                       ~~~~~~~~~~~~~~^~~~
../scipy/optimize/__lbfgsb.c:470:15: warning: passing argument 1 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  470 |         DCOPY(&n, r, &one_int, g, &one_int);
      |               ^~
      |               |
      |               const int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:80:42: note: expected 'int' but argument is of type 'const int *'
   80 | void BLASNAME(cblas_dcopy)(const BLASINT N, const double *X, const BLASINT incX,
      |                            ~~~~~~~~~~~~~~^
../scipy/optimize/__lbfgsb.c:470:22: warning: passing argument 3 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  470 |         DCOPY(&n, r, &one_int, g, &one_int);
      |                      ^~~~~~~~
      |                      |
      |                      int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:80:76: note: expected 'int' but argument is of type 'int *'
   80 | void BLASNAME(cblas_dcopy)(const BLASINT N, const double *X, const BLASINT incX,
      |                                                              ~~~~~~~~~~~~~~^~~~
../scipy/optimize/__lbfgsb.c:470:35: warning: passing argument 5 of 'cblas_dcopy' makes integer from pointer without a cast [-Wint-conversion]
  470 |         DCOPY(&n, r, &one_int, g, &one_int);
      |                                   ^~~~~~~~
      |                                   |
      |                                   int *
../scipy/optimize/../_build_utils/src/npy_cblas_base.h:81:53: note: expected 'int' but argument is of type 'int *'
   81 |                            double *Y, const BLASINT incY);
      |                                       ~~~~~~~~~~~~~~^~~~

So I guess you're building with scipy_openblas32 (and I'm not) and name mangling does not propagate. Does it get any better if you add function prototypes explicitly? I'll play with this, too.

@ilayn
Copy link
Member Author

ilayn commented Aug 6, 2024

I think we have no other option than what special/lapack_...h did.

Or start using LAPACKE properly everywhere.

@ev-br
Copy link
Member

ev-br commented Aug 6, 2024

Nah, I'm pretty sure there's a simpler solution. Will look simple in retrospect, that is :-). I'll take a look.

@ev-br
Copy link
Member

ev-br commented Aug 6, 2024

The following diff makes it build for me locally (bog-standard ubuntu linux, conda env + conda compilers, openblas from conda-forge:

diff --git a/scipy/optimize/__lbfgsb.c b/scipy/optimize/__lbfgsb.c
index 17af196bd6..1ea0cc507d 100644
--- a/scipy/optimize/__lbfgsb.c
+++ b/scipy/optimize/__lbfgsb.c
@@ -1179,10 +1179,10 @@ formk(const int n, const int nsub, const int* ind, const int nenter, const int i
             {
                 js = m + jy;
                 temp_int = m - jy + 1;
-                DCOPY(&temp_int, wn1[(jy + 1) + 2*m*(jy + 1)], &one_int, wn1[jy + 2*m*jy], &one_int);
-                DCOPY(&temp_int, wn1[(js + 1) + 2*m*(js + 1)], &one_int, wn1[js + 2*m*js], &one_int);
+                DCOPY(&temp_int, &wn1[(jy + 1) + 2*m*(jy + 1)], &one_int, &wn1[jy + 2*m*jy], &one_int);
+                DCOPY(&temp_int, &wn1[(js + 1) + 2*m*(js + 1)], &one_int, &wn1[js + 2*m*js], &one_int);
                 temp_int = m - 1;
-                DCOPY(&temp_int, wn1[(m + 1) + 2*m*(jy + 1)], &one_int, wn1[m + 2*m*jy], &one_int);
+                DCOPY(&temp_int, &wn1[(m + 1) + 2*m*(jy + 1)], &one_int, &wn1[m + 2*m*jy], &one_int);
             }
             // 10
         }
@@ -1379,7 +1379,7 @@ formk(const int n, const int nsub, const int* ind, const int nenter, const int i
     {
         for (js = is; js < col2; js++)
         {
-            wn[is + 2*m*js] = wn[is + 2*m*js] + DDOT(col, wn[2*m*is], &one_int, wn[2*m*js], &one_int);
+            wn[is + 2*m*js] = wn[is + 2*m*js] + DDOT(&col, &wn[2*m*is], &one_int, &wn[2*m*js], &one_int);
         }
         // 74
     }
@@ -1727,9 +1727,9 @@ matupd(const int n, const int m, double* ws, double *wy, double* sy, double* ss,
     {
         for (j = 1; j < *col; j++)
         {
-            DCOPY(&j, ss[1 + m*j], &one_int, ss[m*(j-1)], &one_int);
+            DCOPY(&j, &ss[1 + m*j], &one_int, &ss[m*(j-1)], &one_int);
             temp_int = *col - j;
-            DCOPY(&temp_int, sy[j + m*j], &one_int, sy[(j-1) + m*(j-1)], &one_int);
+            DCOPY(&temp_int, &sy[j + m*j], &one_int, &sy[(j-1) + m*(j-1)], &one_int);
         }
         // 50
     }
@@ -1841,7 +1841,7 @@ void subsm(const int n, const int m, const int nsub, const int* ind,
 
     temp = 1.0 / theta;
     // n, da, dx, incx
-    dscal(&nsub, &temp, d, &one_int);
+    DSCAL(&nsub, &temp, d, &one_int);
 
     // -----------------------------------------------------
     // Let us try the projection, d is the Newton direction.
diff --git a/scipy/optimize/__lbfgsb.h b/scipy/optimize/__lbfgsb.h
index 3912c91a99..cffd0c008f 100644
--- a/scipy/optimize/__lbfgsb.h
+++ b/scipy/optimize/__lbfgsb.h
@@ -14,8 +14,19 @@ extern "C"
 #define DNRM2 BLAS_FUNC(dnrm2)
 #define DDOT BLAS_FUNC(ddot)
 #define DCOPY BLAS_FUNC(dcopy)
+#define DSCAL BLAS_FUNC(dscal)
 #define DTRSM BLAS_FUNC(dtrsm)
-#define DPOTRF F_FUNC(dpotrf)
+#define DPOTRF BLAS_FUNC(dpotrf)
+#define DTRTRS BLAS_FUNC(dtrtrs)
+
+void DSCAL(int *n, double *da, double *dx, int *incx);
+double DNRM2(int *n, double *x, int *incx);
+double DDOT(int *n, double *dx, int *incx, double *dy, int *incy);
+void DCOPY(int *n, double *dx, int *incx, double *dy, int *incy);
+void DTRSM(char *side, char *uplo, char *transa, char *diag, int *m, int *n, double *alpha, double *a, int *lda, double *b, int *ldb);
+void DPORTF(char *uplo, int *n, double *a, int *lda, int *info);
+void DTRTRS(char *uplo, char *trans, char *diag, int *n, int *nrhs, double *a, int *lda, double *b, int *ldb, int *info);
+
 
 static PyObject* lbfgsb_error;

This is likely not the end of the story: some of the warnings are definitely relevant (passing char constants instead of int pointers, const-correctness etc etc). Also ran out of time today to check that it actually imports. But there is at least some progress :-).

@ilayn
Copy link
Member Author

ilayn commented Aug 6, 2024

OK so we need the prototypes regardless then. The rest is just unchecked buggy code that I will go through anyways. Thanks a lot

@ev-br
Copy link
Member

ev-br commented Aug 6, 2024

One other thing which looks a tad strange here is that the ModuleDef python glue is in the header file. I'm not claiming it's best practice, just noting that what seemed to work (== compile, link and import) elsewhere is splitting the python module into a separate _module.c file; then you compile the algorithmic part into a static library and use it as a dependency for the dynamic library which is built from a single implementation file contains all the python glue

__fitpack_lib = static_library('__fitpack',
    ['src/__fitpack.h', 'src/__fitpack.cc'],
    dependencies:[lapack, np_dep, py3_dep]
)

__fitpack_dep = declare_dependency(
    link_with: __fitpack_lib,
)

py3.extension_module('_dierckx',
    ['src/_dierckxmodule.cc'],
    include_directories: 'src/',
    dependencies: [py3_dep, np_dep, __fitpack_dep],
    install: true,
    subdir: 'scipy/interpolate'
)

@ilayn
Copy link
Member Author

ilayn commented Aug 6, 2024

That might be true. I just did it in minpack quadpack and others and it worked independently. I think this C stuff is pretty much on consensus and no rules. So I really don't mind keeping them in the same place.

@ev-br
Copy link
Member

ev-br commented Aug 7, 2024

just did it in minpack quadpack and others and it worked independently. I think this C stuff is pretty much on consensus and no rules. So I really don't mind keeping them in the same place.

Sure. As long as it builds, links and imports, whatever works :-). I'm just noticing one difference, maybe irrelevant.

@ilayn
Copy link
Member Author

ilayn commented Aug 7, 2024

I cleaned up a bit so that only LAPACK import complaints are present and C code is not complaining. With the latest commits I get

[3/114] Compiling C object scipy/optimize/_lbfgsb.cp312-win_amd64.pyd.p/__lbfgsb.c.obj
FAILED: scipy/optimize/_lbfgsb.cp312-win_amd64.pyd.p/__lbfgsb.c.obj
"cc" "-Iscipy\optimize\_lbfgsb.cp312-win_amd64.pyd.p" "-Iscipy\optimize" "-I..\scipy\optimize" "-I..\..\..\..\AppData\Local\Programs\Python\Python312\Lib\site-packages\numpy\_core\include" "-IC:/Users/ilhan/AppData/Local/Programs/Python/Python312/Lib/site-packages/scipy_openblas32/include" "-IC:\Users\ilhan\AppData\Local\Programs\Python\Python312\Include" "-fvisibility=hidden" "-fdiagnostics-color=always" "-D_FILE_OFFSET_BITS=64" "-Wall" "-Winvalid-pch" "-std=c17" "-O2" "-g" "-Wno-unused-but-set-variable" "-Wno-unused-function" "-Wno-conversion" "-Wno-misleading-indentation" "-mlong-double-64" "-D__USE_MINGW_ANSI_STDIO=1" "-DBLAS_SYMBOL_PREFIX=scipy_" "-DNPY_NO_DEPRECATED_API=NPY_1_9_API_VERSION" -MD -MQ scipy/optimize/_lbfgsb.cp312-win_amd64.pyd.p/__lbfgsb.c.obj -MF "scipy\optimize\_lbfgsb.cp312-win_amd64.pyd.p\__lbfgsb.c.obj.d" -o scipy/optimize/_lbfgsb.cp312-win_amd64.pyd.p/__lbfgsb.c.obj "-c" ../scipy/optimize/__lbfgsb.c
In file included from ../scipy/optimize/__lbfgsb.h:9,
                 from ../scipy/optimize/__lbfgsb.c:1:
../scipy/optimize/__lbfgsb.c: In function 'formk':
<command-line>: error: implicit declaration of function 'scipy_dpotrf_'; did you mean 'scipy_dtrtrs_'? [-Wimplicit-function-declaration]
../scipy/optimize/../_build_utils/src/npy_cblas.h:50:54: note: in definition of macro 'BLAS_FUNC_CONCAT'
   50 | #define BLAS_FUNC_CONCAT(name,prefix,suffix,suffix2) prefix ## name ## suffix ## suffix2
      |                                                      ^~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:25: note: in expansion of macro 'BLAS_FUNC_EXPAND'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                         ^~~~~~~~~~~~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:47: note: in expansion of macro 'BLAS_SYMBOL_PREFIX'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                                               ^~~~~~~~~~~~~~~~~~
../scipy/optimize/__lbfgsb.h:19:16: note: in expansion of macro 'BLAS_FUNC'
   19 | #define DPOTRF BLAS_FUNC(dpotrf)
      |                ^~~~~~~~~
../scipy/optimize/__lbfgsb.c:1369:5: note: in expansion of macro 'DPOTRF'
 1369 |     DPOTRF(uplo, &col, wn, &m2, info);
      |     ^~~~~~
../scipy/optimize/__lbfgsb.h: At top level:
<command-line>: warning: 'scipy_daxpy_' used but never defined
../scipy/optimize/../_build_utils/src/npy_cblas.h:50:54: note: in definition of macro 'BLAS_FUNC_CONCAT'
   50 | #define BLAS_FUNC_CONCAT(name,prefix,suffix,suffix2) prefix ## name ## suffix ## suffix2
      |                                                      ^~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:25: note: in expansion of macro 'BLAS_FUNC_EXPAND'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                         ^~~~~~~~~~~~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:47: note: in expansion of macro 'BLAS_SYMBOL_PREFIX'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                                               ^~~~~~~~~~~~~~~~~~
../scipy/optimize/__lbfgsb.h:14:15: note: in expansion of macro 'BLAS_FUNC'
   14 | #define DAXPY BLAS_FUNC(daxpy)
      |               ^~~~~~~~~
../scipy/optimize/__lbfgsb.h:23:13: note: in expansion of macro 'DAXPY'
   23 | static void DAXPY(const int* n, const double* da, const double* dx,
      |             ^~~~~
<command-line>: warning: 'scipy_dcopy_' used but never defined
../scipy/optimize/../_build_utils/src/npy_cblas.h:50:54: note: in definition of macro 'BLAS_FUNC_CONCAT'
   50 | #define BLAS_FUNC_CONCAT(name,prefix,suffix,suffix2) prefix ## name ## suffix ## suffix2
      |                                                      ^~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:25: note: in expansion of macro 'BLAS_FUNC_EXPAND'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                         ^~~~~~~~~~~~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:47: note: in expansion of macro 'BLAS_SYMBOL_PREFIX'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                                               ^~~~~~~~~~~~~~~~~~
../scipy/optimize/__lbfgsb.h:15:15: note: in expansion of macro 'BLAS_FUNC'
   15 | #define DCOPY BLAS_FUNC(dcopy)
      |               ^~~~~~~~~
../scipy/optimize/__lbfgsb.h:25:13: note: in expansion of macro 'DCOPY'
   25 | static void DCOPY(const int* n, const double* dx, const int *incx,
      |             ^~~~~
<command-line>: warning: 'scipy_ddot_' used but never defined
../scipy/optimize/../_build_utils/src/npy_cblas.h:50:54: note: in definition of macro 'BLAS_FUNC_CONCAT'
   50 | #define BLAS_FUNC_CONCAT(name,prefix,suffix,suffix2) prefix ## name ## suffix ## suffix2
      |                                                      ^~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:25: note: in expansion of macro 'BLAS_FUNC_EXPAND'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                         ^~~~~~~~~~~~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:47: note: in expansion of macro 'BLAS_SYMBOL_PREFIX'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                                               ^~~~~~~~~~~~~~~~~~
../scipy/optimize/__lbfgsb.h:16:14: note: in expansion of macro 'BLAS_FUNC'
   16 | #define DDOT BLAS_FUNC(ddot)
      |              ^~~~~~~~~
../scipy/optimize/__lbfgsb.h:27:15: note: in expansion of macro 'DDOT'
   27 | static double DDOT(const int *n, const double *dx, const int *incx,
      |               ^~~~
<command-line>: warning: 'scipy_dnrm2_' used but never defined
../scipy/optimize/../_build_utils/src/npy_cblas.h:50:54: note: in definition of macro 'BLAS_FUNC_CONCAT'
   50 | #define BLAS_FUNC_CONCAT(name,prefix,suffix,suffix2) prefix ## name ## suffix ## suffix2
      |                                                      ^~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:25: note: in expansion of macro 'BLAS_FUNC_EXPAND'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                         ^~~~~~~~~~~~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:47: note: in expansion of macro 'BLAS_SYMBOL_PREFIX'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                                               ^~~~~~~~~~~~~~~~~~
../scipy/optimize/__lbfgsb.h:17:15: note: in expansion of macro 'BLAS_FUNC'
   17 | #define DNRM2 BLAS_FUNC(dnrm2)
      |               ^~~~~~~~~
../scipy/optimize/__lbfgsb.h:29:15: note: in expansion of macro 'DNRM2'
   29 | static double DNRM2(const int *n, const double *x, const int *incx);
      |               ^~~~~
<command-line>: warning: 'scipy_dscal_' used but never defined
../scipy/optimize/../_build_utils/src/npy_cblas.h:50:54: note: in definition of macro 'BLAS_FUNC_CONCAT'
   50 | #define BLAS_FUNC_CONCAT(name,prefix,suffix,suffix2) prefix ## name ## suffix ## suffix2
      |                                                      ^~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:25: note: in expansion of macro 'BLAS_FUNC_EXPAND'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                         ^~~~~~~~~~~~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:47: note: in expansion of macro 'BLAS_SYMBOL_PREFIX'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                                               ^~~~~~~~~~~~~~~~~~
../scipy/optimize/__lbfgsb.h:18:15: note: in expansion of macro 'BLAS_FUNC'
   18 | #define DSCAL BLAS_FUNC(dscal)
      |               ^~~~~~~~~
../scipy/optimize/__lbfgsb.h:30:13: note: in expansion of macro 'DSCAL'
   30 | static void DSCAL(const int *n, const double *da, double *dx,
      |             ^~~~~
<command-line>: warning: 'scipy_dtrtrs_' used but never defined
../scipy/optimize/../_build_utils/src/npy_cblas.h:50:54: note: in definition of macro 'BLAS_FUNC_CONCAT'
   50 | #define BLAS_FUNC_CONCAT(name,prefix,suffix,suffix2) prefix ## name ## suffix ## suffix2
      |                                                      ^~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:25: note: in expansion of macro 'BLAS_FUNC_EXPAND'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                         ^~~~~~~~~~~~~~~~
../scipy/optimize/../_build_utils/src/npy_cblas.h:62:47: note: in expansion of macro 'BLAS_SYMBOL_PREFIX'
   62 | #define BLAS_FUNC(name) BLAS_FUNC_EXPAND(name,BLAS_SYMBOL_PREFIX,BLAS_SYMBOL_SUFFIX,BLAS_FORTRAN_SUFFIX)
      |                                               ^~~~~~~~~~~~~~~~~~
../scipy/optimize/__lbfgsb.h:20:16: note: in expansion of macro 'BLAS_FUNC'
   20 | #define DTRTRS BLAS_FUNC(dtrtrs)
      |                ^~~~~~~~~
../scipy/optimize/__lbfgsb.h:34:13: note: in expansion of macro 'DTRTRS'
   34 | static void DTRTRS(const char* uplo, const char *trans, const char *diag,
[10/114] Compiling C++ object scipy/optimize/_highs/libhighs.a.p/.._..__lib_highs_extern_filereaderlp_reader.cpp.obj
ninja: build stopped: subcommand failed.
Build failed!
C:\Users\ilhan\Documents\GitHub\scipy [rewrite_lbfgsb ≡ +0 ~2 -0 !]> 

@ilayn
Copy link
Member Author

ilayn commented Aug 7, 2024

By the way is it blas_dep, lapack_dep or blas, lapack in the meson files?

@ev-br
Copy link
Member

ev-br commented Aug 7, 2024

You seem to be building with scipy-openblas32 wheels. The symbols there have the scipy_ prefix (scipy_dscal etc) and the build system is not informed. Try (the windows analog of)

$ python -c'import scipy_openblas32 as sc; print(sc.get_pkg_config())' > scipy-openblas.pc
$ export PKG_CONFIG_PATH=$PWD
$ python dev.py build --with-scipy-openblas    # builds for me

EDIT: The magic incantation is not really magic, it's just a way to generate a collection of data in a format the build system understands (for one, note the -DBLAS_SYMBOL_PREFIX=scipy_):

$ cat scipy-openblas.pc 
libdir=/home/ev-br/.conda/envs/scipy-dev/lib/python3.11/site-packages/scipy_openblas32/lib
includedir=/home/ev-br/.conda/envs/scipy-dev/lib/python3.11/site-packages/scipy_openblas32/include
openblas_config= OpenBLAS 0.3.27.dev DYNAMIC_ARCH NO_AFFINITY Zen MAX_THREADS=64
version=0.3.27.dev
extralib=-lm -lpthread -lgfortran -lquadmath -L${libdir} -lscipy_openblas
Name: openblas
Description: OpenBLAS is an optimized BLAS library based on GotoBLAS2 1.13 BSD version
Version: ${version}
URL: https://github.com/xianyi/OpenBLAS
Libs: ${libdir}/libscipy_openblas.so -Wl,-rpath,${libdir}
Libs.private: ${extralib}
Cflags: -I${includedir} -DBLAS_SYMBOL_PREFIX=scipy_

@ev-br
Copy link
Member

ev-br commented Sep 15, 2024

The whole point of name mangling is to account for various LAPACK providers: a system openblas, scipy-openblas32, BLIS, MKL etc.

@ilayn
Copy link
Member Author

ilayn commented Sep 15, 2024

Ok can you please run nm on one of the scipy lapack depending binaries? I'm curious what you and I will see in terms of symbols names. Because you are absolutely right

image

What demangles these names I don't know. Somewhere in the codebase, there must be a header that is normalizing all these names.

@mattip
Copy link
Contributor

mattip commented Sep 15, 2024

with scipy-openblas32 on Windows, using an unmagled function prorotype seems to work

It shouldn't. The name-mangling magic for the end-user happens when picking up the scipy-openblas pkgconfig from Cflags: -I${includedir} -DBLAS_SYMBOL_PREFIX=scipy_. Without that prefix, you should not be able to link to the importlib. Maybe somehow you are using BLAS_SYMBOL_PREFIX?

@mattip
Copy link
Contributor

mattip commented Sep 15, 2024

Could you give a complete example where using void dpotrf_(char* uplo, int* n, double* a, int* lda, int* info); compiles and links to the libscipy_openblas.lib in scipy-openblas32 on windows?

@ilayn
Copy link
Member Author

ilayn commented Sep 15, 2024

In this PR, in the header file we have bunch of BLAS LAPACK prototypes and all CI jobs somehow seem to pickup and compile this. Hence our confusion, especially on windows jobs. Also I am building with scipy-openblas32 on my own machine and it goes through.

@ilayn
Copy link
Member Author

ilayn commented Sep 16, 2024

What do you think folks, should we kick this one in? I am a bit anxious about the LAPACK parts, so the sooner we know the better we can work on the fix.

@ev-br
Copy link
Member

ev-br commented Sep 16, 2024

Retried rebuilding with locally with$ python dev.py --with-scipy-openblas. Builds fine and all tests pass, including SCIPY_XSLOW=1 ones. So I guess we can land this PR and iterate :-).

The LAPACK linking story is mysterious:

  • _lbfgs extension does link to scipy_openblas.so and not the system openblas:
$ ldd build/scipy/optimize/_lbfgsb.cpython-311-x86_64-linux-gnu.so
	linux-vdso.so.1 (0x00007ffcb19dd000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f7f35cd2000)
	libscipy_openblas.so => /home/ev-br/.conda/envs/scipy-dev/lib/python3.11/site-packages/scipy_openblas32/lib/libscipy_openblas.so (0x00007f7f348de000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7f346b5000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f7f35dde000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f7f346b0000)
	libgfortran-040039e1.so.5.0.0 => /home/ev-br/.conda/envs/scipy-dev/lib/python3.11/site-packages/scipy_openblas32/lib/./libgfortran-040039e1.so.5.0.0 (0x00007f7f34200000)
	libquadmath-96973f99.so.0.0.0 => /home/ev-br/.conda/envs/scipy-dev/lib/python3.11/site-packages/scipy_openblas32/lib/./libquadmath-96973f99.so.0.0.0 (0x00007f7f33e00000)
	libz.so.1 => /home/ev-br/.conda/envs/scipy-dev/lib/libz.so.1 (0x00007f7f341e5000)
	libgcc_s.so.1 => /home/ev-br/.conda/envs/scipy-dev/lib/libgcc_s.so.1 (0x00007f7f341c6000)
  • _lbfgs extension contains an unmangled symbol and an "undefined" mangled symbol:
$ nm build/scipy/optimize/_lbfgsb.cpython-311-x86_64-linux-gnu.so |grep dpotrf
0000000000007fe0 t dpotrf_
                 U scipy_dpotrf_
  • scipy_openblas library does not have an unmangled symbol (unless .localalias is the culprit -- no idea what it is though):
$ nm /home/ev-br/.conda/envs/scipy-dev/lib/python3.11/site-packages/scipy_openblas32/lib/libscipy_openblas.so |grep dpotrf
0000000000b1d160 t dpotrf2_.localalias
00000000003130d0 T dpotrf_L_parallel
0000000000312870 T dpotrf_L_single
0000000000312e10 T dpotrf_U_parallel
0000000000312260 T dpotrf_U_single
000000000111e4a0 T scipy_LAPACKE_dpotrf
000000000111e710 T scipy_LAPACKE_dpotrf2
000000000111e7a0 T scipy_LAPACKE_dpotrf2_work
000000000111e530 T scipy_LAPACKE_dpotrf_work
0000000000b1d160 T scipy_dpotrf2_
00000000000cf180 T scipy_dpotrf_

So I've no idea what is dpotrf_ and where it comes from.

@ilayn
Copy link
Member Author

ilayn commented Sep 16, 2024

I think BLAS_SYMBOL_PREFIX is indeed the secret sauce as @mattip explained

DOC:optimize: Added license and docstrings of LBFGSB C code

BUG:optimize: Fix segfaults and 1-off errors in LBFGSB code

BUG:optimize: Address segfaults in subsm C func
DOC:optimize: Fix LBFGSB docstring

MAINT:optimize: Use int array for task and status messages in LBFGSB
DOC:MAINT:Also removed Fortran mentions

MAINT:optimize: Fix LBFGSB termination messages

MAINT: optimize: Modify Python file for LBFGSB rewrite
TST:optimize: Modify tests for LBFGSB rewrite
@ilayn
Copy link
Member Author

ilayn commented Sep 16, 2024

OK squashed and separated files for later cherry-picking. This is good to go.

@ev-br
Copy link
Member

ev-br commented Sep 16, 2024

I think BLAS_SYMBOL_PREFIX is indeed the secret sauce as @mattip explained

Yes, but it does not seem to be used (the scipy_openblas PREFIX is scipy_).

@ilayn
Copy link
Member Author

ilayn commented Sep 16, 2024

I think I don't have enough tools in my belt to understand this mechanism. It seems to me that something is not demangling but something is actually adding the prefix but how does it understand which one to add and search openblas eludes me.

@adam-sim-dev
Copy link

Hi @ilayn , thanks for the excellent work! Any plans to remove gotos in the code?

@ilayn
Copy link
Member Author

ilayn commented Nov 8, 2024

Hi @adam-sim-dev I considered removing them completely but the remaining gotos are not that bad (about 20 of them left from a large number), typically jumping to the cleanly separated code block. The original authors used it for flow control, though still not fun to have them, removing them leads to unnecessary code duplication if we don't put something else in place.

My hope is that now that it is a bit more readable and streamlined, someone sits down and writes this in a cleaner procedural way meaning introducing new smaller functions instead of gotos. My focus was to steer away from F77 isms so I did not dare to introduce new bugs because of that. But if you have any appetite, it would be much appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C/C++ Items related to the internal C/C++ code base enhancement A new feature or improvement Fortran Items related to the internal Fortran code base maintenance Items related to regular maintenance tasks Meson Items related to the introduction of Meson as the new build system for SciPy scipy.optimize
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants