Scippy

    SCIP

    Solving Constraint Integer Programs

    lapack_calls.c
    Go to the documentation of this file.
    1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    2/* */
    3/* This file is part of the program and library */
    4/* SCIP --- Solving Constraint Integer Programs */
    5/* */
    6/* Copyright (c) 2002-2025 Zuse Institute Berlin (ZIB) */
    7/* */
    8/* Licensed under the Apache License, Version 2.0 (the "License"); */
    9/* you may not use this file except in compliance with the License. */
    10/* You may obtain a copy of the License at */
    11/* */
    12/* http://www.apache.org/licenses/LICENSE-2.0 */
    13/* */
    14/* Unless required by applicable law or agreed to in writing, software */
    15/* distributed under the License is distributed on an "AS IS" BASIS, */
    16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
    17/* See the License for the specific language governing permissions and */
    18/* limitations under the License. */
    19/* */
    20/* You should have received a copy of the Apache-2.0 license */
    21/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
    22/* */
    23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    24
    25/**@file lapack_calls.h
    26 * @brief interface methods for lapack functions
    27 * @author Marc Pfetsch
    28 *
    29 * This file is used to call the LAPACK routine DSYEVR and DGETRF.
    30 *
    31 * LAPACK can be built with 32- or 64-bit integers, which is not visible to the outside. This interface tries to work
    32 * around this issue. Since the Fortran routines are called by reference, they only get a pointer. We always use 64-bit
    33 * integers on input, but reduce the output to 32-bit integers. We assume that all sizes can be represented in 32-bit
    34 * integers.
    35 */
    36
    37/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
    38
    39#include <assert.h>
    40
    41#include "scip/lapack_calls.h"
    42
    43#include "scip/def.h"
    44#include "scip/pub_message.h" /* for debug and error message */
    46#include "scip/type_retcode.h"
    47#include "scip/nlpi_ipopt.h"
    48
    49/* turn off lint warnings for whole file: */
    50/*lint --e{788,818}*/
    51
    52
    53#ifdef SCIP_WITH_LAPACK
    54/* we use 64 bit integers as the base type */
    55typedef int64_t LAPACKINTTYPE;
    56
    57/** transforms a SCIP_Real (that should be integer, but might be off by some numerical error) to an integer by adding 0.5 and rounding down */
    58#define SCIP_RealTOINT(x) ((LAPACKINTTYPE) (x + 0.5))
    59
    60/*
    61 * BLAS/LAPACK Calls
    62 */
    63
    64/**@name BLAS/LAPACK Calls */
    65/**@{ */
    66
    67/** Define to a macro mangling the given C identifier (in lower and upper
    68 * case), which must not contain underscores, for linking with Fortran. */
    69#ifdef FNAME_LCASE_DECOR
    70#define F77_FUNC(name,NAME) name ## _
    71#endif
    72#ifdef FNAME_UCASE_DECOR
    73#define F77_FUNC(name,NAME) NAME ## _
    74#endif
    75#ifdef FNAME_LCASE_NODECOR
    76#define F77_FUNC(name,NAME) name
    77#endif
    78#ifdef FNAME_UCASE_NODECOR
    79#define F77_FUNC(name,NAME) NAME
    80#endif
    81
    82/* use backup ... */
    83#ifndef F77_FUNC
    84#define F77_FUNC(name,NAME) name ## _
    85#endif
    86
    87
    88/** LAPACK Fortran subroutine DSYEVR */
    89void F77_FUNC(dsyevr, DSYEVR)(char* JOBZ, char* RANGE, char* UPLO,
    90 LAPACKINTTYPE* N, SCIP_Real* A, LAPACKINTTYPE* LDA,
    91 SCIP_Real* VL, SCIP_Real* VU,
    92 LAPACKINTTYPE* IL, LAPACKINTTYPE* IU,
    93 SCIP_Real* ABSTOL, LAPACKINTTYPE* M, SCIP_Real* W, SCIP_Real* Z,
    94 LAPACKINTTYPE* LDZ, LAPACKINTTYPE* ISUPPZ, SCIP_Real* WORK,
    95 LAPACKINTTYPE* LWORK, LAPACKINTTYPE* IWORK, LAPACKINTTYPE* LIWORK,
    96 LAPACKINTTYPE* INFO);
    97
    98/** LAPACK Fortran subroutine DGETRF */
    99void F77_FUNC(dgetrf, DGETRF)(LAPACKINTTYPE* M, LAPACKINTTYPE* N, SCIP_Real* A,
    100 LAPACKINTTYPE* LDA, LAPACKINTTYPE* IPIV, LAPACKINTTYPE* INFO);
    101
    102/** LAPACK Fortran subroutine DGETRS */
    103void F77_FUNC(dgetrs, DGETRS)(char* TRANS, LAPACKINTTYPE* N, LAPACKINTTYPE* NRHS,
    104 SCIP_Real* A, LAPACKINTTYPE* LDA, LAPACKINTTYPE* IPIV, SCIP_Real* B, LAPACKINTTYPE* LDB,
    105 LAPACKINTTYPE* INFO);
    106
    107/** LAPACK Fortran subroutine ivlayer */
    108void F77_FUNC(ilaver, ILAVER)(LAPACKINTTYPE* MAJOR, LAPACKINTTYPE* MINOR, LAPACKINTTYPE* PATCH);
    109
    110/**@} */
    111#endif
    112
    113/*
    114 * Functions
    115 */
    116
    117/**@name Functions */
    118/**@{ */
    119
    120/** returns whether Lapack is available, i.e., whether it has been linked in */
    122{
    124 return TRUE;
    125
    126#ifdef SCIP_WITH_LAPACK
    127 return TRUE;
    128#else
    129 return FALSE;
    130#endif
    131}
    132
    133#ifdef SCIP_WITH_LAPACK
    134/** converts a number stored in a int64_t to an int, depending on big- or little endian machines
    135 *
    136 * We assume that the number actually fits into an int. Thus, if more bits are used, we assume that the number is
    137 * negative.
    138 */
    139static
    140int convertToInt(
    141 int64_t num /**< number to be converted */
    142 )
    143{
    144 union
    145 {
    146 int64_t big;
    147 int small[2];
    148 } work;
    149 int checkval = 1;
    150
    151 assert(sizeof(work) > sizeof(checkval)); /*lint !e506*/
    152
    153 work.big = num;
    154
    155 /* if we have a little-endian machine (e.g, x86), the sought value is in the bottom part */
    156 if ( *(int8_t*)&checkval != 0 ) /*lint !e774*/ /* cppcheck-suppress knownConditionTrueFalse */
    157 {
    158 /* if the top part is nonzero, we assume that the number is negative */
    159 if ( work.small[1] != 0 ) /*lint !e2662*/
    160 {
    161 work.big = -num;
    162 return -work.small[0];
    163 }
    164 return work.small[0];
    165 }
    166
    167 /* otherwise we have a big-endian machine (e.g., PowerPC); the sought value is in the top part */
    168 assert( *(int8_t*)&checkval == 0 );
    169
    170 /* if the bottom part is nonzero, we assume that the number is negative */
    171 if ( work.small[0] != 0 ) /*lint !e774*/
    172 {
    173 work.big = -num;
    174 return -work.small[1]; /*lint !e2662*/
    175 }
    176 return work.small[1];
    177}
    178#endif
    179
    180/** returns whether Lapack s available, i.e., whether it has been linked in */
    182 int* majorver, /**< major version number */
    183 int* minorver, /**< minor version number */
    184 int* patchver /**< patch version number */
    185 )
    186{
    187#ifdef SCIP_WITH_LAPACK
    188 LAPACKINTTYPE MAJOR = 0LL;
    189 LAPACKINTTYPE MINOR = 0LL;
    190 LAPACKINTTYPE PATCH = 0LL;
    191#endif
    192
    193 assert( majorver != NULL );
    194 assert( minorver != NULL );
    195 assert( patchver != NULL );
    196
    197#ifdef SCIP_WITH_LAPACK
    198 F77_FUNC(ilaver, ILAVER)(&MAJOR, &MINOR, &PATCH);
    199
    200 *majorver = convertToInt(MAJOR);
    201 *minorver = convertToInt(MINOR);
    202 *patchver = convertToInt(PATCH);
    203#else
    204 *majorver = -1;
    205 *minorver = -1;
    206 *patchver = -1;
    207#endif
    208}
    209
    210#ifdef SCIP_WITH_LAPACK
    211/** computes eigenvalues of a symmetric matrix using LAPACK */
    212static
    213SCIP_RETCODE lapackComputeEigenvalues(
    214 BMS_BUFMEM* bufmem, /**< buffer memory */
    215 SCIP_Bool geteigenvectors, /**< Should also the eigenvectors be computed? */
    216 int n, /**< size of matrix */
    217 SCIP_Real* A, /**< matrix data on input (size n * n); eigenvectors on output if geteigenvectors == TRUE */
    218 SCIP_Real* eigenvalues /**< pointer to store eigenvalue */
    219 )
    220{
    221 LAPACKINTTYPE* IWORK;
    222 LAPACKINTTYPE* ISUPPZ;
    223 LAPACKINTTYPE N;
    224 LAPACKINTTYPE INFO;
    225 LAPACKINTTYPE LDA;
    226 LAPACKINTTYPE WISIZE;
    227 LAPACKINTTYPE IL;
    228 LAPACKINTTYPE IU;
    229 LAPACKINTTYPE M;
    230 LAPACKINTTYPE LDZ;
    231 LAPACKINTTYPE LWORK;
    232 LAPACKINTTYPE LIWORK;
    233 SCIP_Real* WORK;
    234 SCIP_Real* WTMP;
    235 SCIP_Real* Z = NULL;
    236 SCIP_Real ABSTOL;
    237 SCIP_Real WSIZE;
    238 SCIP_Real VL;
    239 SCIP_Real VU;
    240 char JOBZ;
    241 char RANGE;
    242 char UPLO;
    243
    244 assert( bufmem != NULL );
    245 assert( n > 0 );
    246 assert( n < INT_MAX );
    247 assert( A != NULL );
    248 assert( eigenvalues != NULL );
    249
    250 N = n;
    251 JOBZ = geteigenvectors ? 'V' : 'N';
    252 RANGE = 'A';
    253 UPLO = 'L';
    254 LDA = n;
    255 ABSTOL = 0.0; /* we use abstol = 0, since some lapack return an error otherwise */
    256 VL = -1e20;
    257 VU = 1e20;
    258 IL = 0;
    259 IU = n;
    260 M = n;
    261 LDZ = n;
    262 INFO = 0LL;
    263
    264 /* standard LAPACK workspace query, to get the amount of needed memory */
    265 LWORK = -1LL;
    266 LIWORK = -1LL;
    267
    268 /* this computes the internally needed memory and returns this as (the first entries of [the 1x1 arrays]) WSIZE and WISIZE */
    269 F77_FUNC(dsyevr, DSYEVR)( &JOBZ, &RANGE, &UPLO,
    270 &N, NULL, &LDA,
    271 NULL, NULL,
    272 &IL, &IU,
    273 &ABSTOL, &M, NULL, NULL,
    274 &LDZ, NULL, &WSIZE,
    275 &LWORK, &WISIZE, &LIWORK,
    276 &INFO);
    277
    278 if ( convertToInt(INFO) != 0 )
    279 {
    280 SCIPerrorMessage("There was an error when calling DSYEVR. INFO = %d.\n", convertToInt(INFO));
    281 return SCIP_ERROR;
    282 }
    283
    284 /* allocate workspace */
    285 LWORK = SCIP_RealTOINT(WSIZE);
    286 LIWORK = MAX(1, 10 * N); /* the size is also returned in WISIZE, but is always equal to the value used here */
    287
    288 SCIP_ALLOC( BMSallocBufferMemoryArray(bufmem, &WORK, (int) LWORK) );
    289 SCIP_ALLOC( BMSallocBufferMemoryArray(bufmem, &IWORK, (int) LIWORK) );
    290 SCIP_ALLOC( BMSallocBufferMemoryArray(bufmem, &WTMP, (int) N) );
    291 SCIP_ALLOC( BMSallocBufferMemoryArray(bufmem, &ISUPPZ, 2 * n) ); /*lint !e506*/
    292 if ( geteigenvectors )
    293 {
    294 SCIP_ALLOC( BMSallocBufferMemoryArray(bufmem, &Z, n * n) ); /*lint !e647*/
    295 }
    296
    297 /* call the function */
    298 F77_FUNC(dsyevr, DSYEVR)( &JOBZ, &RANGE, &UPLO,
    299 &N, A, &LDA,
    300 &VL, &VU,
    301 &IL, &IU,
    302 &ABSTOL, &M, WTMP, Z,
    303 &LDZ, ISUPPZ, WORK,
    304 &LWORK, IWORK, &LIWORK,
    305 &INFO);
    306
    307 /* handle output */
    308 if ( convertToInt(INFO) == 0 )
    309 {
    310 int m;
    311 int i;
    312 int j;
    313
    314 m = convertToInt(M);
    315 for (i = 0; i < m; ++i)
    316 eigenvalues[i] = WTMP[i];
    317 for (i = m; i < n; ++i)
    318 eigenvalues[i] = SCIP_INVALID;
    319
    320 /* possibly overwrite matrix with eigenvectors */
    321 if ( geteigenvectors )
    322 {
    323 for (i = 0; i < m; ++i)
    324 {
    325 for (j = 0; j < n; ++j)
    326 A[i * n + j] = Z[i * n + j];
    327 }
    328 }
    329 }
    330
    331 /* free memory */
    333 BMSfreeBufferMemoryArray(bufmem, &ISUPPZ);
    334 BMSfreeBufferMemoryArray(bufmem, &WTMP);
    335 BMSfreeBufferMemoryArray(bufmem, &IWORK);
    336 BMSfreeBufferMemoryArray(bufmem, &WORK);
    337
    338 if ( convertToInt(INFO) != 0 )
    339 {
    340 SCIPerrorMessage("There was an error when calling DSYEVR. INFO = %d.\n", convertToInt(INFO));
    341 return SCIP_ERROR;
    342 }
    343
    344 return SCIP_OKAY;
    345}
    346#endif
    347
    348/** computes eigenvalues and eigenvectors of a dense symmetric matrix
    349 *
    350 * Calls Lapack's DSYEV function.
    351 */
    353 BMS_BUFMEM* bufmem, /**< buffer memory (or NULL if IPOPT is used) */
    354 SCIP_Bool geteigenvectors, /**< should also eigenvectors should be computed? */
    355 int N, /**< dimension */
    356 SCIP_Real* a, /**< matrix data on input (size N*N); eigenvectors on output if geteigenvectors == TRUE */
    357 SCIP_Real* w /**< array to store eigenvalues (size N) (or NULL) */
    358 )
    359{
    360 /* if IPOPT is available, call its LAPACK routine */
    362 {
    363 SCIP_CALL( SCIPcallLapackDsyevIpopt(geteigenvectors, N, a, w) );
    364 }
    365 else
    366 {
    367 assert( bufmem != NULL );
    368#ifdef SCIP_WITH_LAPACK
    369 SCIP_CALL( lapackComputeEigenvalues(bufmem, geteigenvectors, N, a, w) );
    370#else
    371 SCIPerrorMessage("Lapack not available.\n");
    372 return SCIP_PLUGINNOTFOUND;
    373#endif
    374 }
    375
    376 return SCIP_OKAY;
    377}
    378
    379/** solves a linear problem of the form Ax = b for a regular matrix A
    380 *
    381 * Calls Lapacks DGETRF routine to calculate a LU factorization and uses this factorization to solve
    382 * the linear problem Ax = b.
    383 *
    384 * Code taken from nlpi_ipopt.cpp
    385 */
    387 BMS_BUFMEM* bufmem, /**< buffer memory */
    388 int n, /**< dimension */
    389 SCIP_Real* A, /**< matrix data on input (size N*N); filled column-wise */
    390 SCIP_Real* b, /**< right hand side vector (size N) */
    391 SCIP_Real* x, /**< buffer to store solution (size N) */
    392 SCIP_Bool* success /**< pointer to store if the solving routine was successful */
    393 )
    394{
    395 assert( n > 0 );
    396 assert( A != NULL );
    397 assert( b != NULL );
    398 assert( x != NULL );
    399 assert( success != NULL );
    400
    401 /* if possible, use IPOPT */
    403 {
    404 SCIP_CALL( SCIPsolveLinearEquationsIpopt(n, A, b, x, success) );
    405 }
    406 else
    407 {
    408#ifdef SCIP_WITH_LAPACK
    409 LAPACKINTTYPE INFO;
    410 LAPACKINTTYPE N;
    411 LAPACKINTTYPE* pivots;
    412 SCIP_Real* Atmp = NULL;
    413 SCIP_Real* btmp = NULL;
    414
    415 assert( bufmem != NULL );
    416
    417 SCIP_ALLOC( BMSduplicateBufferMemoryArray(bufmem, &Atmp, A, n * n) ); /*lint !e647*/
    418 SCIP_ALLOC( BMSduplicateBufferMemoryArray(bufmem, &btmp, b, n) );
    419 SCIP_ALLOC( BMSallocBufferMemoryArray(bufmem, &pivots, n) );
    420
    421 /* compute LU factorization */
    422 N = n;
    423 F77_FUNC(dgetrf, DGETRF)(&N, &N, Atmp, &N, pivots, &INFO);
    424
    425 if ( convertToInt(INFO) != 0 )
    426 {
    427 SCIPdebugMessage("There was an error when calling DGETRF. INFO = %d\n", convertToInt(INFO));
    428 *success = FALSE;
    429 }
    430 else
    431 {
    432 LAPACKINTTYPE NRHS = 1LL;
    433 char TRANS = 'N';
    434
    435 /* solve system */
    436 F77_FUNC(dgetrs, DGETRS)(&TRANS, &N, &NRHS, Atmp, &N, pivots, btmp, &N, &INFO);
    437
    438 if ( convertToInt(INFO) != 0 )
    439 {
    440 SCIPdebugMessage("There was an error when calling DGETRF. INFO = %d\n", convertToInt(INFO));
    441 *success = FALSE;
    442 }
    443 else
    444 *success = TRUE;
    445
    446 /* copy the solution */
    447 BMScopyMemoryArray(x, btmp, n);
    448 }
    449
    450 BMSfreeBufferMemoryArray(bufmem, &pivots);
    451 BMSfreeBufferMemoryArray(bufmem, &btmp);
    452 BMSfreeBufferMemoryArray(bufmem, &Atmp);
    453#else
    454 SCIP_UNUSED(bufmem);
    455
    456 /* call fallback solution in nlpi_ipopt_dummy */
    457 SCIP_CALL( SCIPsolveLinearEquationsIpopt(n, A, b, x, success) );
    458#endif
    459 }
    460
    461 return SCIP_OKAY;
    462}
    463
    464/**@} */
    SCIP_VAR * w
    Definition: circlepacking.c:67
    SCIP_VAR * a
    Definition: circlepacking.c:66
    SCIP_VAR ** b
    Definition: circlepacking.c:65
    SCIP_VAR ** x
    Definition: circlepacking.c:63
    common defines and data types used in all packages of SCIP
    #define NULL
    Definition: def.h:248
    #define SCIP_UNUSED(x)
    Definition: def.h:409
    #define SCIP_INVALID
    Definition: def.h:178
    #define SCIP_Bool
    Definition: def.h:91
    #define SCIP_ALLOC(x)
    Definition: def.h:366
    #define SCIP_Real
    Definition: def.h:156
    #define TRUE
    Definition: def.h:93
    #define FALSE
    Definition: def.h:94
    #define MAX(x, y)
    Definition: def.h:220
    #define SCIP_CALL(x)
    Definition: def.h:355
    SCIP_RETCODE SCIPcallLapackDsyevIpopt(SCIP_Bool computeeigenvectors, int N, SCIP_Real *a, SCIP_Real *w)
    SCIP_RETCODE SCIPsolveLinearEquationsIpopt(int N, SCIP_Real *A, SCIP_Real *b, SCIP_Real *x, SCIP_Bool *success)
    SCIP_Bool SCIPisIpoptAvailableIpopt(void)
    SCIP_Bool SCIPlapackIsAvailable(void)
    Definition: lapack_calls.c:121
    SCIP_RETCODE SCIPlapackComputeEigenvalues(BMS_BUFMEM *bufmem, SCIP_Bool geteigenvectors, int N, SCIP_Real *a, SCIP_Real *w)
    Definition: lapack_calls.c:352
    SCIP_RETCODE SCIPlapackSolveLinearEquations(BMS_BUFMEM *bufmem, int n, SCIP_Real *A, SCIP_Real *b, SCIP_Real *x, SCIP_Bool *success)
    Definition: lapack_calls.c:386
    void SCIPlapackVersion(int *majorver, int *minorver, int *patchver)
    Definition: lapack_calls.c:181
    interface methods for lapack functions
    memory allocation routines
    #define BMSduplicateBufferMemoryArray(mem, ptr, source, num)
    Definition: memory.h:737
    #define BMSfreeBufferMemoryArray(mem, ptr)
    Definition: memory.h:742
    #define BMScopyMemoryArray(ptr, source, num)
    Definition: memory.h:134
    #define BMSallocBufferMemoryArray(mem, ptr, num)
    Definition: memory.h:731
    #define BMSfreeBufferMemoryArrayNull(mem, ptr)
    Definition: memory.h:743
    void F77_FUNC(filtersqp, FILTERSQP)
    Ipopt NLP interface.
    public methods for message output
    #define SCIPerrorMessage
    Definition: pub_message.h:64
    #define SCIPdebugMessage
    Definition: pub_message.h:96
    type definitions for return codes for SCIP methods
    @ SCIP_PLUGINNOTFOUND
    Definition: type_retcode.h:54
    @ SCIP_OKAY
    Definition: type_retcode.h:42
    @ SCIP_ERROR
    Definition: type_retcode.h:43
    enum SCIP_Retcode SCIP_RETCODE
    Definition: type_retcode.h:63