blas_classic.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  *
8  * CasADi is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 3 of the License, or (at your option) any later version.
12  *
13  * CasADi is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with CasADi; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  *
22  */
23 
24 
25 #include "blas_classic.hpp"
26 #include "casadi/core/code_generator.hpp"
27 #include "casadi/core/exception.hpp"
28 #include "casadi/core/runtime/casadi_runtime.hpp"
29 
30 #include <climits>
31 #include <cstring>
32 
33 namespace casadi {
34 
35  static void classic_dgemm(int transa, int transb,
36  casadi_int m, casadi_int n, casadi_int k,
37  double alpha,
38  const double* A, casadi_int lda,
39  const double* B, casadi_int ldb,
40  double beta,
41  double* C, casadi_int ldc) {
42  // Fortran BLAS-32 takes int by reference. Guard against ILP64 / huge
43  // problems before silently truncating.
44  casadi_assert(m <= INT_MAX && n <= INT_MAX && k <= INT_MAX
45  && lda <= INT_MAX && ldb <= INT_MAX && ldc <= INT_MAX,
46  "BLAS 'classic' plugin: matrix dimension exceeds 32-bit BLAS ABI. "
47  "Rebuild against an ILP64 BLAS or use the \"reference\" plugin.");
48 
49  const char ta = (transa == CASADI_BLAS_TRANS) ? 'T' : 'N';
50  const char tb = (transb == CASADI_BLAS_TRANS) ? 'T' : 'N';
51  int m_ = static_cast<int>(m), n_ = static_cast<int>(n), k_ = static_cast<int>(k);
52  int lda_ = static_cast<int>(lda), ldb_ = static_cast<int>(ldb), ldc_ = static_cast<int>(ldc);
53 
54  dgemm_(&ta, &tb, &m_, &n_, &k_, &alpha,
55  A, &lda_, B, &ldb_, &beta, C, &ldc_);
56  }
57 
58  static void classic_daxpy(casadi_int n, double alpha,
59  const double* x, double* y) {
60  casadi_assert(n <= INT_MAX,
61  "BLAS 'classic' plugin: vector length exceeds 32-bit BLAS ABI.");
62  int n_ = static_cast<int>(n), inc = 1;
63  daxpy_(&n_, &alpha, x, &inc, y, &inc);
64  }
65 
66  static double classic_ddot(casadi_int n,
67  const double* x, const double* y) {
68  casadi_assert(n <= INT_MAX,
69  "BLAS 'classic' plugin: vector length exceeds 32-bit BLAS ABI.");
70  int n_ = static_cast<int>(n), inc = 1;
71  return ddot_(&n_, x, &inc, y, &inc);
72  }
73 
74  static void classic_dscal(casadi_int n, double alpha, double* x) {
75  casadi_assert(n <= INT_MAX,
76  "BLAS 'classic' plugin: vector length exceeds 32-bit BLAS ABI.");
77  int n_ = static_cast<int>(n), inc = 1;
78  dscal_(&n_, &alpha, x, &inc);
79  }
80 
81  static double classic_dnrm2(casadi_int n, const double* x) {
82  casadi_assert(n <= INT_MAX,
83  "BLAS 'classic' plugin: vector length exceeds 32-bit BLAS ABI.");
84  int n_ = static_cast<int>(n), inc = 1;
85  return dnrm2_(&n_, x, &inc);
86  }
87 
88  static double classic_dasum(casadi_int n, const double* x) {
89  casadi_assert(n <= INT_MAX,
90  "BLAS 'classic' plugin: vector length exceeds 32-bit BLAS ABI.");
91  int n_ = static_cast<int>(n), inc = 1;
92  return dasum_(&n_, x, &inc);
93  }
94 
95  static void classic_dcopy(const double* x, casadi_int n, double* y) {
96  if (!y) return;
97  if (x) std::memcpy(y, x, n * sizeof(double));
98  else std::memset(y, 0, n * sizeof(double));
99  }
100 
101  static const char* CLASSIC_DECL =
102  "/* BLAS \"classic\" plugin: Fortran ABI dgemm_, link with -lblas/-lopenblas/-lmkl. */\n"
103  "/* Override the symbol at compile time with -DCASADI_BLAS_DGEMM=my_dgemm. */\n"
104  "#ifndef CASADI_BLAS_DGEMM\n"
105  "#define CASADI_BLAS_DGEMM dgemm_\n"
106  "#endif\n"
107  "extern void CASADI_BLAS_DGEMM(const char* transa, const char* transb,\n"
108  " const int* m, const int* n, const int* k,\n"
109  " const double* alpha,\n"
110  " const double* A, const int* lda,\n"
111  " const double* B, const int* ldb,\n"
112  " const double* beta,\n"
113  " double* C, const int* ldc);";
114 
115  static const char* CLASSIC_DAXPY_DECL =
116  "#ifndef CASADI_BLAS_DAXPY\n"
117  "#define CASADI_BLAS_DAXPY daxpy_\n"
118  "#endif\n"
119  "extern void CASADI_BLAS_DAXPY(const int* n, const double* alpha,\n"
120  " const double* x, const int* incx,\n"
121  " double* y, const int* incy);";
122 
123  static const char* CLASSIC_DDOT_DECL =
124  "#ifndef CASADI_BLAS_DDOT\n"
125  "#define CASADI_BLAS_DDOT ddot_\n"
126  "#endif\n"
127  "extern double CASADI_BLAS_DDOT(const int* n,\n"
128  " const double* x, const int* incx,\n"
129  " const double* y, const int* incy);";
130 
131  static const char* CLASSIC_DSCAL_DECL =
132  "#ifndef CASADI_BLAS_DSCAL\n"
133  "#define CASADI_BLAS_DSCAL dscal_\n"
134  "#endif\n"
135  "extern void CASADI_BLAS_DSCAL(const int* n, const double* alpha,\n"
136  " double* x, const int* incx);";
137 
138  static const char* CLASSIC_DNRM2_DECL =
139  "#ifndef CASADI_BLAS_DNRM2\n"
140  "#define CASADI_BLAS_DNRM2 dnrm2_\n"
141  "#endif\n"
142  "extern double CASADI_BLAS_DNRM2(const int* n, const double* x, const int* incx);";
143 
144  static const char* CLASSIC_DASUM_DECL =
145  "#ifndef CASADI_BLAS_DASUM\n"
146  "#define CASADI_BLAS_DASUM dasum_\n"
147  "#endif\n"
148  "extern double CASADI_BLAS_DASUM(const int* n, const double* x, const int* incx);";
149 
151  const std::string& A, casadi_int m, casadi_int k,
152  const std::string& B, casadi_int n, const std::string& C) {
154  // Locals shared with other Fortran-ABI BLAS plugins. g.local is
155  // idempotent on (name,type) match, so multiple plugins coexisting in
156  // the same Function reuse the same locals safely.
157  g.local("blas_tn", "char"); g.init_local("blas_tn", "'N'");
158  g.local("blas_one", "double"); g.init_local("blas_one", "1.0");
159  g.local("blas_m", "int");
160  g.local("blas_n", "int");
161  g.local("blas_k", "int");
162  g << "blas_m = " << m << "; blas_n = " << n << "; blas_k = " << k << ";\n";
163  g << "CASADI_BLAS_DGEMM(&blas_tn, &blas_tn, &blas_m, &blas_n, &blas_k, "
164  "&blas_one, " << A << ", &blas_m, " << B << ", &blas_k, "
165  "&blas_one, " << C << ", &blas_m);\n";
166  }
167 
168 
170  const std::vector<std::string>& inst) {
171  static const char* SRC =
172  "// SYMBOL \"axpy\"\n"
173  "void casadi_axpy(casadi_int n, casadi_real alpha,\n"
174  " const casadi_real* x, casadi_real* y) {\n"
175  " int n_ = (int)n, inc = 1;\n"
176  " CASADI_BLAS_DAXPY(&n_, &alpha, x, &inc, y, &inc);\n"
177  "}\n";
178  g.auxiliaries << CLASSIC_DAXPY_DECL << "\n";
179  g.auxiliaries << g.sanitize_source(SRC, inst);
180  }
181 
183  const std::vector<std::string>& inst) {
184  static const char* SRC =
185  "// SYMBOL \"scal\"\n"
186  "void casadi_scal(casadi_int n, casadi_real alpha, casadi_real* x) {\n"
187  " int n_ = (int)n, inc = 1;\n"
188  " CASADI_BLAS_DSCAL(&n_, &alpha, x, &inc);\n"
189  "}\n";
190  g.auxiliaries << CLASSIC_DSCAL_DECL << "\n";
191  g.auxiliaries << g.sanitize_source(SRC, inst);
192  }
193 
195  const std::vector<std::string>& inst) {
196  static const char* SRC =
197  "// SYMBOL \"dot\"\n"
198  "casadi_real casadi_dot(casadi_int n,\n"
199  " const casadi_real* x, const casadi_real* y) {\n"
200  " int n_ = (int)n, inc = 1;\n"
201  " return CASADI_BLAS_DDOT(&n_, x, &inc, y, &inc);\n"
202  "}\n";
203  g.auxiliaries << CLASSIC_DDOT_DECL << "\n";
204  g.auxiliaries << g.sanitize_source(SRC, inst);
205  }
206 
208  const std::vector<std::string>& inst) {
209  static const char* SRC =
210  "// SYMBOL \"norm_2\"\n"
211  "casadi_real casadi_norm_2(casadi_int n, const casadi_real* x) {\n"
212  " int n_ = (int)n, inc = 1;\n"
213  " return CASADI_BLAS_DNRM2(&n_, x, &inc);\n"
214  "}\n";
215  g.auxiliaries << CLASSIC_DNRM2_DECL << "\n";
216  g.auxiliaries << g.sanitize_source(SRC, inst);
217  }
218 
220  const std::vector<std::string>& inst) {
221  // Match casadi_norm_1<T>'s null-pointer semantics: returns 0 on x==NULL.
222  static const char* SRC =
223  "// SYMBOL \"norm_1\"\n"
224  "casadi_real casadi_norm_1(casadi_int n, const casadi_real* x) {\n"
225  " int n_ = (int)n, inc = 1;\n"
226  " if (!x) return 0;\n"
227  " return CASADI_BLAS_DASUM(&n_, x, &inc);\n"
228  "}\n";
229  g.auxiliaries << CLASSIC_DASUM_DECL << "\n";
230  g.auxiliaries << g.sanitize_source(SRC, inst);
231  }
232 
233  extern "C" int CASADI_BLAS_CLASSIC_EXPORT
234  casadi_register_blas_classic(Blas::Plugin* plugin) {
235  plugin->name = "classic";
236  plugin->doc = BlasClassic::meta_doc.c_str();
237  plugin->version = CASADI_VERSION;
238  plugin->exposed.dgemm = &classic_dgemm;
239  plugin->exposed.codegen_mtimes = &classic_codegen_mtimes;
240  plugin->exposed.daxpy = &classic_daxpy;
241  plugin->exposed.ddot = &classic_ddot;
242  plugin->exposed.dscal = &classic_dscal;
243  plugin->exposed.dnrm2 = &classic_dnrm2;
244  plugin->exposed.dasum = &classic_dasum;
245  plugin->exposed.dcopy = &classic_dcopy;
246  plugin->exposed.codegen_axpy_aux = &classic_codegen_axpy_aux;
247  plugin->exposed.codegen_dot_aux = &classic_codegen_dot_aux;
248  plugin->exposed.codegen_scal_aux = &classic_codegen_scal_aux;
249  plugin->exposed.codegen_nrm2_aux = &classic_codegen_nrm2_aux;
250  plugin->exposed.codegen_asum_aux = &classic_codegen_asum_aux;
251  plugin->options = nullptr;
252  plugin->deserialize = nullptr;
253  plugin->creator = nullptr;
254  return 0;
255  }
256 
257  extern "C" void CASADI_BLAS_CLASSIC_EXPORT casadi_load_blas_classic() {
259  }
260 
261 #if defined(CASADI_CORE_BLAS_DEPENDENCY) && defined(CASADI_L1_BLAS)
262  extern "C" void CASADI_BLAS_CLASSIC_EXPORT casadi_blas_classic_set_l1_hooks() {
263  casadi_daxpy_hook = &classic_daxpy;
264  casadi_ddot_hook = &classic_ddot;
265  casadi_dscal_hook = &classic_dscal;
266  casadi_dnrm2_hook = &classic_dnrm2;
267  casadi_dasum_hook = &classic_dasum;
268  casadi_dcopy_hook = &classic_dcopy;
269  }
270 #endif
271 
272 } // namespace casadi
static const std::string meta_doc
A documentation string.
Helper class for C code generation.
void local(const std::string &name, const std::string &type, const std::string &ref="")
Declare a local variable.
void add_external(const std::string &new_external)
Add an external function declaration.
void init_local(const std::string &name, const std::string &def)
Specify the default value for a local variable.
std::string sanitize_source(const std::string &src, const std::vector< std::string > &inst, bool add_shorthand=true)
Sanitize source files for codegen.
std::stringstream auxiliaries
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
The casadi namespace.
Definition: archiver.cpp:28
static const char * CLASSIC_DAXPY_DECL
static const char * CLASSIC_DSCAL_DECL
@ CASADI_BLAS_TRANS
Definition: blas_impl.hpp:42
static void classic_codegen_nrm2_aux(CodeGenerator &g, const std::vector< std::string > &inst)
void CASADI_BLAS_CLASSIC_EXPORT casadi_load_blas_classic()
static void classic_codegen_asum_aux(CodeGenerator &g, const std::vector< std::string > &inst)
static const char * CLASSIC_DECL
static const char * CLASSIC_DNRM2_DECL
static void classic_codegen_dot_aux(CodeGenerator &g, const std::vector< std::string > &inst)
static double classic_dasum(casadi_int n, const double *x)
static const char * CLASSIC_DASUM_DECL
static void classic_codegen_mtimes(CodeGenerator &g, const std::string &A, casadi_int m, casadi_int k, const std::string &B, casadi_int n, const std::string &C)
static void classic_dcopy(const double *x, casadi_int n, double *y)
int CASADI_BLAS_CLASSIC_EXPORT casadi_register_blas_classic(Blas::Plugin *plugin)
static void classic_dgemm(int transa, int transb, casadi_int m, casadi_int n, casadi_int k, double alpha, const double *A, casadi_int lda, const double *B, casadi_int ldb, double beta, double *C, casadi_int ldc)
static double classic_ddot(casadi_int n, const double *x, const double *y)
static void classic_codegen_axpy_aux(CodeGenerator &g, const std::vector< std::string > &inst)
static void classic_daxpy(casadi_int n, double alpha, const double *x, double *y)
static void classic_codegen_scal_aux(CodeGenerator &g, const std::vector< std::string > &inst)
static void classic_dscal(casadi_int n, double alpha, double *x)
static double classic_dnrm2(casadi_int n, const double *x)
static const char * CLASSIC_DDOT_DECL