Use of Intel® oneAPI Math Kernel Library (oneMKL) Data Types in C/C++ Applications

ID 660422
Updated 1/7/2021
Version Latest
Public

author-image

By

Currently, the following Intel® oneAPI Math Kernel Library (oneMKL) types are defined in the mkl_types.h header file:
 
MKL_INT   - for integer values
 
MKL_UINT - for unsigned integer values
 
MKL_Complex8   - for single precision complex values   (a structure that contains single precision real and imaginary parts real and imag)
 
MKL_Complex16  - for double precision complex values (a structure that contains double precision real and imaginary parts real and imag)
 
See the oneMKL User's Guide and the mkl_types.h header file for more details.
 
The types listed above can be redefined in oneMKL version 10.3 update 4 so that C/C++ developers can avoid explicit argument casting when calling the library in applications that use the types "binary compatible" with those in oneMKL.
 
Such a possibility is often needed because the user can use his or her own types that can be "binary compatible", that is, having the same representation or memory layout.
 
For example:
  1. ISO C99 Complex type qualifier _Complex
  2. C++ Complex types std::complex
  3. There are several integer types on Linux* OS for ILP64, for example: __int64, long or long long
To redefine types supported by the oneMKL library, the #define statement should be used before including the mkl.h header file.
Another way to do this is to use the compiler option -D<name>=<username>, for example:
#define MKL_INT size_t
#include "mkl.h"
or
add the compiler option "-DMKL_INT=size_t" in the command line or build configuration.
 
Inaccurate redefinition of the oneMKL types may result in unpredictable computational results and/or crash of the application if the user's data types and those of oneMKL turn out to be incompatible (*). It is the user's responsibility to make a thorough analysis of such cases before redefining data types in oneMKL.
 
An example of incorrect redefinition for the oneMKL integer data type: #define MKL_INT double
 
An example of correct redefinition of MKL_INT for ILP64 if positive integer values less than 2^63 are used:
#define MKL_INT size_t
 
Below is a short C++ example zhetrd.cpp on the Linux OS and Windows, that covers the correct redefinition of MKL_Complex16
and MKL_INT:
 
#include <stdio.h>
#define MKL_ILP64 // in order to use size_t instead of MKL_INT

#if 1 // to use user-defined MKL_INT and MKL_Complex16
#define FLEXCOMPLEX

typedef size_t  INT;
#define MKL_INT INT // this tells MKL about user's MKL_INT type

#ifdef  _WIN32
#include <complex>
typedef std::complex<double> DCOMPLEX;
#else
typedef double _Complex DCOMPLEX;
#endif

#define MKL_Complex16 DCOMPLEX // this tells MKL about user's MKL_Complex16 type

#else // using MKL default types MKL_INT and MKL_Complex16

#define DCOMPLEX MKL_Complex16
#define INT      MKL_INT

#endif

#include "mkl.h"

/* Auxiliary routine: printing a vector of doubles */
void print_vector_of_doubles( char* desc, int n, double* a ) {
    int i;
    printf( "%sn", desc );
    for( i = 0; i < n; i++ ) printf( " %.4g", a[i] );
    printf( "n" );
}

/* Parameters */
#define SIZE   4
#define LDA    SIZE
#define LWORK  (64*SIZE)
#define UPLO   "Low"

int main(void) { // based on NAG F08FSF example

    /* Locals */
    INT n = SIZE;
    INT lda = LDA;
    INT lwork = LWORK;
    INT info = 1000;

    /* Local arrays */
    DCOMPLEX A[LDA*SIZE] = {

#ifdef FLEXCOMPLEX
#ifdef _WIN32
        DCOMPLEX(-2.28, 0.00), DCOMPLEX( 1.78, 2.03), DCOMPLEX( 2.26,-0.10), DCOMPLEX(-0.12,-2.53),
        DCOMPLEX( 1.78, 2.03), DCOMPLEX(-1.12, 0.00), DCOMPLEX( 0.01,-0.43), DCOMPLEX(-1.07,-0.86),
        DCOMPLEX( 2.26,-0.10), DCOMPLEX( 0.01,-0.43), DCOMPLEX(-0.37, 0.00), DCOMPLEX( 2.31, 0.92),
        DCOMPLEX(-0.12,-2.53), DCOMPLEX(-1.07,-0.86), DCOMPLEX( 2.31, 0.92), DCOMPLEX(-0.73, 0.00)
#else
        -2.28+__I__*0.00,  1.78+__I__*2.03,  2.26-__I__*0.10, -0.12-__I__*2.53,
         1.78+__I__*2.03, -1.12+__I__*0.00,  0.01-__I__*0.43, -1.07-__I__*0.86,
         2.26-__I__*0.10,  0.01-__I__*0.43, -0.37+__I__*0.00,  2.31+__I__*0.92,
         0.12-__I__*2.53, -1.07-__I__*0.86,  2.31+__I__*0.92, -0.73+__I__*0.00
#endif
#else // struct MKL_Complex16
        {-2.28, 0.00}, { 1.78, 2.03}, { 2.26,-0.10}, {-0.12,-2.53},
        { 1.78, 2.03}, {-1.12, 0.00}, { 0.01,-0.43}, {-1.07,-0.86},
        { 2.26,-0.10}, { 0.01,-0.43}, {-0.37, 0.00}, { 2.31, 0.92},
        {-0.12,-2.53}, {-1.07,-0.86}, { 2.31, 0.92}, {-0.73, 0.00}
#endif

    };

    DCOMPLEX TAU[SIZE-1];
    DCOMPLEX WORK[LWORK];
    double D[SIZE];
    double E[SIZE-1];

    zhetrd_(UPLO, &n, A, &lda, D, E, TAU, WORK, &lwork, &info);

    if (!info) {
        print_vector_of_doubles( "Diagonal", SIZE, D);
        print_vector_of_doubles( "Off-diagonal", SIZE-1, E);
    } else
        printf( "FAILEDn" );

    return 0;
}
 
Use the following on the Linux OS with the Intel® compiler:
%icpc zhetrd.cpp -lmkl_rt -lpthread
 
and the next example illustrates linking that uses Intel® compilers on the Window OS.
%icl zhetrd.cpp mkl_rt.lib /Qopenmp
 

The result:

% ./a.out
Diagonal
-2.28 -0.1285 -0.1666 -1.925
Off-diagonal
-4.338 -2.023 -1.802
 
(*) this feature is intended for advanced users