- Contents
- Operation index
- Introduction
- Object management
- Computational function reference
- Query function reference
- Example code
This index provides a quick way to jump directly to the description for each operation discussed later in the Computational function reference section:
- Level-1v: Operations on vectors:
- Level-1d: Element-wise operations on matrix diagonals:
- Level-1m: Element-wise operations on matrices:
- Level-1f: Fused operations on multiple vectors:
- Level-2: Operations with one matrix and (at least) one vector operand:
- Level-3: Operations with matrices that are multiplication-like:
- Utility: Miscellaneous operations on matrices and vectors:
This document summarizes one of the primary native APIs in BLIS--the object API. Here, we also discuss BLIS-specific type definitions, header files, and prototypes to auxiliary functions.
There are many functions that BLIS implements that are not listed here, either because they are lower-level functions, or they are considered for use primarily by developers and experts.
The object API was given its name (a) because it abstracts the floating-point types of its operands (along with many other properties) within a typedef struct {...}
data structure, and (b) to contrast it with the other native API in BLIS, the typed API, which is documented here. (The third API supported by BLIS is the BLAS compatibility layer, which mimics conventional Fortran-77 BLAS.)
In general, this document should be treated more as a reference than a place to learn how to use BLIS in your application. Thus, we highly encourage all readers to first study the example code provided within the BLIS source distribution.
The following tables list various types used throughout the BLIS object API.
BLIS integer type | Type definition | Used to represent... |
---|---|---|
gint_t |
int32_t or int64_t |
general-purpose signed integer; used to define signed integer types. |
guint_t |
uint32_t or uint64_t |
general-purpose signed integer; used to define signed integer types. |
dim_t |
gint_t |
matrix and vector dimensions. |
inc_t |
gint_t |
matrix row/column strides and vector increments. |
doff_t |
gint_t |
matrix diagonal offset: if k < 0, diagonal begins at element (-k,0); otherwise diagonal begins at element (0,k). |
siz_t |
guint_t |
a byte size or byte offset. |
BLIS fp type | Type definition | Used to represent... |
---|---|---|
float |
N/A | single-precision real numbers. |
double |
N/A | double-precision real numbers. |
scomplex |
struct { float real; float imag; } |
single-precision complex numbers. |
dcomplex |
struct { double real; double imag; } |
double-precision complex numbers. |
num_t |
Semantic meaning: Matrix/vector operand... |
---|---|
BLIS_FLOAT |
contains single-precision real elements. |
BLIS_DOUBLE |
contains double-precision real elements. |
BLIS_SCOMPLEX |
contains single-precision complex elements. |
BLIS_DCOMPLEX |
contains double-precision complex elements. |
BLIS_INT |
contains integer elements of type gint_t . |
BLIS_CONSTANT |
contains polymorphic representation of a constant value. |
dom_t |
Semantic meaning: Matrix/vector operand... |
---|---|
BLIS_REAL |
contains real domain elements. |
BLIS_COMPLEX |
contains complex domain elements. |
prec_t |
Semantic meaning: Matrix/vector operand... |
---|---|
BLIS_SINGLE_PREC |
contains single-precision elements. |
BLIS_DOUBLE_PREC |
contains double-precision elements. |
trans_t |
Semantic meaning: Matrix operand ... |
---|---|
BLIS_NO_TRANSPOSE |
will be used as given. |
BLIS_TRANSPOSE |
will be implicitly transposed. |
BLIS_CONJ_NO_TRANSPOSE |
will be implicitly conjugated. |
BLIS_CONJ_TRANSPOSE |
will be implicitly transposed and conjugated. |
conj_t |
Semantic meaning: Matrix/vector operand... |
---|---|
BLIS_NO_CONJUGATE |
will be used as given. |
BLIS_CONJUGATE |
will be implicitly conjugated. |
side_t |
Semantic meaning: Matrix operand... |
---|---|
BLIS_LEFT |
appears on the left. |
BLIS_RIGHT |
appears on the right. |
struc_t |
Semantic meaning: Matrix operand... |
---|---|
BLIS_GENERAL |
has no structure. |
BLIS_HERMITIAN |
has Hermitian structure. |
BLIS_SYMMETRIC |
has symmetric structure. |
BLIS_TRIANGULAR |
has triangular structure. |
uplo_t |
Semantic meaning: Matrix operand... |
---|---|
BLIS_LOWER |
is stored in (and will be accessed only from) the lower triangle. |
BLIS_UPPER |
is stored in (and will be accessed only from) the upper triangle. |
BLIS_DENSE |
is stored as a full matrix (ie: in both triangles). |
diag_t |
Semantic meaning: Matrix operand ... |
---|---|
BLIS_NONUNIT_DIAG |
has a non-unit diagonal that should be explicitly read from. |
BLIS_UNIT_DIAG |
has a unit diagonal that should be implicitly assumed (and not read from). |
BLIS defines a handful of scalar objects that conveniently represent various constant values for all defined numerical type values (num_t
). The following table lists the constants defined by BLIS.
BLIS constant obj_t name |
Numerical values |
---|---|
BLIS_MINUS_TWO |
-2.0 |
BLIS_MINUS_ONE |
-1.0 |
BLIS_ZERO |
0.0 |
BLIS_ONE |
1.0 |
BLIS_TWO |
2.0 |
These objects are polymorphic; each one contains a float
, double
, scomplex
, dcomplex
, and gint_t
representation of the constant value in question. They can be used in place of any obj_t*
operand in any object API function provided that the following criteria are met:
- The object parameter requires unit dimensions (1x1). (In other words, the function expects a scalar for the operand in question.)
- The object parameter is input-only. (In other words, the function is not trying to update the scalar.)
The correct representation is chosen by context, usually by inspecting the datatype of one of the other operands involved in an operation. For example, if we create and initialize objects
x
andy
ofnum_t
typeBLIS_DOUBLE
, the following call tobli_axpyv()
will use thebli_axpyv( &BLIS_TWO, &x, &y );
BLIS_DOUBLE
representation ofBLIS_TWO
.
The functions listed in this document belong to the "basic" interface subset of the BLIS object API. There is a companion "expert" interface that mirrors the basic interface, except that it also contains two additional parameters that are only of interest to experts and library developers. The expert interfaces use the same name as the basic function names, except for an additional "_ex" suffix. For example, the basic interface for gemm
is
void bli_gemm
(
obj_t* alpha,
obj_t* a,
obj_t* b,
obj_t* beta,
obj_t* c,
);
while the expert interface is:
void bli_gemm_ex
(
obj_t* alpha,
obj_t* a,
obj_t* b,
obj_t* beta,
obj_t* c,
cntx_t* cntx,
rntm_t* rntm
);
The expert interface contains two additional parameters: a cntx_t*
and rntm_t*
. Note that calling a function from the expert interface with the cntx_t*
and rntm_t*
arguments each set to NULL
is equivalent to calling the corresponding basic interface. Specifically, a NULL
value passed in for the cntx_t*
results in a valid context being queried from BLIS, and a NULL
value passed in for the rntm_t*
results in the current global settings for multithreading to be used.
In general, it is permissible to pass in NULL
for a cntx_t*
parameter when calling an expert interface such as bli_gemm_ex()
. However, there are cases where NULL
values are not accepted and may result in a segmentation fault. Specifically, the cntx_t*
argument appears in the interfaces to the gemm
, trsm
, and gemmtrsm
level-3 microkernels along with all level-1v and level-1f kernels. There, as a general rule, a valid pointer must be passed in. Whenever a valid context is needed, the developer may query a default context from the global kernel structure (if a context is not already available in the current scope):
cntx_t* bli_gks_query_cntx( void );
When BLIS is configured to target a configuration family (e.g. intel64
, x86_64
), bli_gks_query_cntx()
will use cpuid
or an equivalent heuristic to select and and return the appropriate context. When BLIS is configured to target a singleton sub-configuration (e.g. haswell
, skx
), bli_gks_query_cntx()
will unconditionally return a pointer to the context appropriate for the targeted configuration.
When calling one of the expert interfaces, a rntm_t
(runtime) object can be used to convey a thread-local request for parallelism to the underlying implementation. Runtime objects are thread-safe by nature when they are declared statically as a stack variable (or allocated via malloc()
), initialized, and then passed into the expert interface of interest.
Notice that runtime objects have no analogue in most BLAS libraries, where you are forced to specify parallelism at a global level (usually via environment variables).
For more information on using rntm_t
objects, please read the Multithreading documentation, paying close attention to the section on local setting of parallelism.
All BLIS definitions and prototypes may be included in your C source file by including a single header file:
#include "blis.h"
As of 9804adf, BLIS no longer requires explicit initialization and finalization at runtime. In other words, users do not need to call bli_init()
before the application can make use of the library (and bli_finalize()
after the application is finished with the library). Instead, all computational operations (and some non-computational functions) in BLIS will initialize the library on behalf of the user if it has not already been initialized. This change was made to simplify the user experience.
Application developers should keep in mind, however, that this new self-initialization regime implies the following: unless the library is explicitly finalized via bli_finalize()
, it will, once initialized, remain initialized for the life of the application. This is likely not a problem in the vast majority of cases. However, a memory-constrained application that performs all of its DLA up-front, for example, may wish to explicitly finalize the library after BLIS is no longer needed in order to free up memory for other purposes.
Similarly, an expert user may call bli_init()
manually in order to control when the overhead of library initialization is incurred, even though the library would have self-initialized.
The interfaces to bli_init()
and bli_finalize()
are quite simple; they require no arguments and return no values:
void bli_init( void );
void bli_finalize( void );
Before using the object API, you must first create some objects to encapsulate your vector or matrix data. We provide examples code for creating matrix objects in the examples/oapi directory of the BLIS source distribution. However, we will provide API documentation for the most common functions for creating and freeing objects in the next section.
Generally speaking, an object is created when an obj_t
structure is initialized with valid properties describing the object as well as a valid data buffer (to hold the elements of the vector or matrix). The valid data buffer can be allocated automatically on your behalf at the same time that the other object fields are initialized, or "attached" in a second step after the object is initialized with preliminary values. The former is useful when using the object API at the setup stage of an application (and if malloc()
is an acceptable method of allocating memory). Similarly, the latter is useful when interfacing BLIS into the middle of an application after the allocation has already taken place, or when some function other than malloc()
is desired for allocating the buffer.
Only objects that were created with automatic allocation must be freed via BLIS object API. Objects that were initialized with attached buffers can be freed in whatever manner is appropriate, based on how the application originally allocated the memory in question.
void bli_obj_create
(
num_t dt,
dim_t m,
dim_t n,
inc_t rs,
inc_t cs,
obj_t* obj
);
Initialize an m x n object obj
and allocate sufficient storage to hold mn elements whose storage type is specified by dt
and with row and column strides rs
and cs
, respectively. This function allocates enough space to enforce alignment of leading dimensions, where the alignment factor is specific to the configuration being used, though the alignment factor is almost always equal to the size of the hardware's SIMD registers.
The address obj
must reference valid memory--typically an obj_t
declared statically or allocated dynamically via malloc()
.
After an object created via bli_obj_create()
is no longer needed, it should be deallocated via bli_obj_free()
.
void bli_obj_free
(
obj_t* obj
);
Deallocate (release) an object obj
that was previously created, typically via bli_obj_create()
.
void bli_obj_create_without_buffer
(
num_t dt,
dim_t m,
dim_t n,
obj_t* obj
);
Partially initialize an m x n object obj
that will eventually contain elements whose storage type is specified by dt
. This function does not result in any memory allocation. Before obj
can be used, the object must be fully initialized by attaching a buffer via bli_obj_attach_buffer()
. This function is useful when the user wishes to encapsulate existing buffers into one or more obj_t
objects.
An object (partially) initialized via this function should generally not be passed to bli_obj_free()
even after a buffer is attached to it via bli_obj_attach_buffer()
, unless the user wishes to pass that buffer into free()
.
void bli_obj_attach_buffer
(
void* p,
inc_t rs,
inc_t cs,
inc_t is,
obj_t* obj
);
Given a partially initialized object (i.e., one that has already been passed to bli_obj_create_without_buffer()
), attach the buffer pointed to by p
to the object referenced by obj
and initialize obj
as containing elements with row and column strides rs
and cs
, respectively. The function also initializes the imaginary stride as is
, which is experimental and not consistently used by all parts of BLIS.
void bli_obj_create_with_attached_buffer
(
num_t dt,
dim_t m,
dim_t n,
void* p,
inc_t rs,
inc_t cs,
obj_t* obj
);
Initialize an m x n object obj
as containing mn elements whose storage type is specified by dt
and with row and column strides rs
and cs
, respectively. The function does not allocate any memory and instead attaches the buffer pointed to by p
. Note that calling this function is effectively equivalent to calling
bli_obj_create_without_buffer( dt, m, n, obj );
bli_obj_attach_buffer( p, rs, cs, 1, obj );
Objects initialized via this function should generally not be passed to bli_obj_free()
, unless the user wishes to pass p
into free()
.
void bli_obj_alloc_buffer
(
inc_t rs,
inc_t cs,
inc_t is,
obj_t* obj
);
Given a partially initialized m x n object, allocate and attach a buffer large enough to contain mn elements with the row and column strides rs
and cs
, respectively. This function allocates enough space to enforce alignment of leading dimensions, where the alignment factor is specific to the configuration being used, though the alignment factor is almost always equal to the size of the hardware's SIMD registers.
Note that calling bli_obj_create()
is effectively equivalent to calling
bli_obj_create_without_buffer( dt, m, n, obj );
bli_obj_alloc_buffer( rs, cs, 1, obj );
Very few users will likely have a need to call this function. We provide documentation for it mostly so that others can manually access the alignment features of bli_obj_create()
without also needing to initialize an obj_t
.
void bli_obj_create_1x1
(
num_t dt,
obj_t* obj
);
Initialize a 1 x 1 object obj
and allocate sufficient storage to hold one element whose storage type is specified by dt
.
The address obj
must reference valid memory--typically an obj_t
declared statically or allocated dynamically via malloc()
.
This function is useful any time the user wishes to create a scalar object with an allocated buffer.
Note that calling bli_obj_create_1x1()
is effectively equivalent to calling
bli_obj_create_without_buffer( dt, 1, 1, obj );
bli_obj_alloc_buffer( 1, 1, 1, obj );
After an object created via bli_obj_create_1x1()
is no longer needed, it should be deallocated via bli_obj_free()
.
void bli_obj_create_1x1_with_attached_buffer
(
num_t dt,
void* p,
obj_t* obj
);
Initialize a 1 x 1 object obj
as containing one element whose storage type is specified by dt
. The function does not allocate any memory and instead attaches the buffer pointed to by p
. Note that calling this function is effectively equivalent to calling
bli_obj_create_without_buffer( dt, 1, 1, obj );
bli_obj_attach_buffer( p, 1, 1, 1, obj );
Objects initialized via this function should generally not be passed to bli_obj_free()
, unless the user wishes to pass p
into free()
.
void bli_obj_create_conf_to
(
obj_t* s,
obj_t* d
);
Initialize an object d
with dimensions conformal to those of an existing object s
. Object d
is initialized with the same row and column strides as those of s
. However, the structure, uplo, conjugation, and transposition properties of s
are not inherited by d
.
On entry, object s
must be fully initialized and the address d
must reference valid memory--typically an obj_t
declared statically or allocated dynamically via malloc()
.
Note that calling this function is effectively equivalent to calling
num_t dt = bli_obj_dt( s );
dim_t m = bli_obj_length( s );
dim_t n = bli_obj_width( s );
inc_t rs = bli_obj_row_stride( s );
inc_t cs = bli_obj_col_stride( s );
bli_obj_create( dt, m, n, rs, cs, d );
After an object created via bli_obj_create_conf_to()
is no longer needed, it should be deallocated via bli_obj_free()
.
void bli_obj_scalar_init_detached
(
num_t dt,
obj_t* obj
);
Initialize a 1 x 1 object obj
using internal storage sufficient to hold one element whose storage type is specified by dt
. (Internal storage is present within every obj_t
and is capable of holding on element of any supported type.) This function is similar to bli_obj_create_1x1()
, except that the object does not trigger any dynamic memory allocation.
Objects initialized via this function should never be passed to bli_obj_free()
.
Notes for interpreting function descriptions:
- Object accessor functions allow the caller to query certain properties of objects.
- These functions are only guaranteed to return meaningful values when called upon objects that have been fully initialized/created.
- Many specialized functions are omitted from this section for brevity. For a full list of accessor functions, please see frame/include/bli_obj_macro_defs.h, though most users will most likely not need methods beyond those documented below.
num_t bli_obj_dt( obj_t* obj );
Return the storage datatype property of obj
.
dom_t bli_obj_domain( obj_t* obj );
Return the domain component of the storage datatype property of obj
.
prec_t bli_obj_prec( obj_t* obj );
Return the precision component of the storage datatype property of obj
.
trans_t bli_obj_conjtrans_status( obj_t* obj );
Return the trans_t
property of obj
, which may indicate transposition, conjugation, both, or neither. Thus, possible return values are BLIS_NO_TRANSPOSE
, BLIS_CONJ_NO_TRANSPOSE
, BLIS_TRANSPOSE
, or BLIS_CONJ_TRANSPOSE
.
trans_t bli_obj_onlytrans_status( obj_t* obj );
Return the transposition component of the trans_t
property of obj
, which may indicate transposition or no transposition.
Thus, possible return values are BLIS_NO_TRANSPOSE
or BLIS_TRANSPOSE
.
conj_t bli_obj_conj_status( obj_t* obj );
Return the conjugation component of the trans_t
property of obj
, which may indicate conjugation or no conjugation.
Thus, possible return values are BLIS_NO_CONJUGATE
or BLIS_CONJUGATE
.
struc_t bli_obj_struc( obj_t* obj );
Return the structure property of obj
.
uplo_t bli_obj_uplo( obj_t* obj );
Return the uplo (i.e., storage) property of obj
.
diag_t bli_obj_diag( obj_t* obj );
Return the diagonal property of obj
.
doff_t bli_obj_diag_offset( obj_t* obj );
Return the diagonal offset of obj
. Note that the diagonal offset will be negative, -i
, if the diagonal begins at element (-i,0)
and positive j
if the diagonal begins at element (0,j)
.
dim_t bli_obj_length( obj_t* obj );
Return the number of rows (or m dimension) of obj
. This value is the m dimension before taking into account the transposition property as indicated by bli_obj_onlytrans_status()
or bli_obj_conjtrans_status()
.
dim_t bli_obj_width( obj_t* obj );
Return the number of columns (or n dimension) of obj
. This value is the n dimension before taking into account the transposition property as indicated by bli_obj_onlytrans_status()
or bli_obj_conjtrans_status()
.
dim_t bli_obj_length_after_trans( obj_t* obj );
Return the number of rows (or m dimension) of obj
after taking into account the transposition property as indicated by bli_obj_onlytrans_status()
or bli_obj_conjtrans_status()
.
dim_t bli_obj_width_after_trans( obj_t* obj );
Return the number of columns (or n dimension) of obj
after taking into account the transposition property as indicated by bli_obj_onlytrans_status()
or bli_obj_conjtrans_status()
.
inc_t bli_obj_row_stride( obj_t* obj );
Return the row stride property of obj
. When storing by columns, the row stride is 1. When storing by rows, the row stride is also sometimes called the leading dimension.
inc_t bli_obj_col_stride( obj_t* obj );
Return the column stride property of obj
. When storing by rows, the column stride is 1. When storing by columns, the column stride is also sometimes called the leading dimension.
dim_t bli_obj_vector_dim( obj_t* obj );
Return the number of elements in a vector object obj
.
This function assumes that at least one dimension of obj
is unit, and that it therefore represents a vector.
inc_t bli_obj_vector_inc( obj_t* obj );
Return the storage increment of a vector object obj
.
This function assumes that at least one dimension of obj
is unit, and that it therefore represents a vector.
void* bli_obj_buffer( obj_t* obj );
Return the address to the data buffer associated with object obj
.
Note: The address returned by this buffer will not take into account any subpartitioning. However, this will not be a problem for most casual users.
siz_t bli_obj_elem_size( obj_t* obj );
Return the size, in bytes, of the storage datatype as indicated by bli_obj_dt()
.
Notes for interpreting function descriptions:
- Object mutator functions allow the caller to modify certain properties of objects.
- The user should be extra careful about modifying properties after objects are created. For typical use of these functions, please study the example code provided in examples/oapi.
- The list of mutators below is much shorter than the list of accessor functions provided in the previous section. Most mutator functions should not be called by users (unless you know what you are doing). For a full list of mutator functions, please see frame/include/bli_obj_macro_defs.h, though most users will most likely not need methods beyond those documented below.
void bli_obj_set_conjtrans( trans_t trans, obj_t* obj );
Set both conjugation and transposition properties of obj
using the corresponding components of trans
.
void bli_obj_set_onlytrans( trans_t trans, obj_t* obj );
Set the transposition property of obj
using the transposition component of trans
. Leaves the conjugation property of obj
unchanged.
void bli_obj_set_conj( conj_t conj, obj_t* obj );
Set the conjugation property of obj
using conj
. Leaves the transposition property of obj
unchanged.
void bli_obj_apply_trans( trans_t trans, obj_t* obj );
Apply trans
to the transposition property of obj
. For example, applying BLIS_TRANSPOSE
will toggle the transposition property of obj
but leave the conjugation property unchanged; applying BLIS_CONJ_TRANSPOSE
will toggle both the conjugation and transposition properties of obj
.
void bli_obj_apply_conj( conj_t conj, obj_t* obj );
Apply conj
to the conjugation property of obj
. Specifically, applying BLIS_CONJUGATE
will toggle the conjugation property of obj
; applying BLIS_NO_CONJUGATE
will have no effect. Leaves the transposition property of obj
unchanged.
void bli_obj_set_struc( struc_t struc, obj_t* obj );
Set the structure property of obj
to struc
.
void bli_obj_set_uplo( uplo_t uplo, obj_t* obj );
Set the uplo (i.e., storage) property of obj
to uplo
.
void bli_obj_set_diag( diag_t diag, obj_t* obj );
Set the diagonal property of obj
to diag
.
void bli_obj_set_diag_offset( doff_t doff, obj_t* obj );
Set the diagonal offset property of obj
to doff
. Note that doff_t
may be typecast from any signed integer.
void bli_obj_induce_trans( obj_t* obj );
Modify the properties of obj
to induce a logical transposition. This function operates without regard to whether the transposition property is already set. Therefore, depending on the circumstance, the caller may or may not wish to clear the transposition property after calling this function.
void bli_obj_alias_to( obj_t* a, obj_t* b );
Initialize b
to be a shallow copy, or alias, of a
. For most people's purposes, this is equivalent to
b = a;
However, there is at least one field (one that only developers should be concerned with) that is not copied.
void bli_obj_real_part( obj_t* c, obj_t* r );
Initialize r
to be a modified shallow copy of c
that refers only to the real part of c
.
void bli_obj_imag_part( obj_t* c, obj_t* i );
Initialize i
to be a modified shallow copy of c
that refers only to the imaginary part of c
.
Notes for interpreting function descriptions:
conj?(X)
andtrans?(X)
should be interpreted as predicates that capture the operandX
with that object'sconj_t
ortrans_t
property applied. For example:conj?(x)
refers to a vectorx
that is either conjugated or used as given.trans?(A)
refers to a matrixA
that is either transposed, conjugated and transposed, conjugated only, or used as given.
- Any operand marked with
conj()
is unconditionally conjugated. - Any operand marked with
^T
is unconditionally transposed. Similarly, any operand that is marked with^H
is unconditionally conjugate-transposed. - All occurrences of
alpha
,beta
, andrho
parameters are scalars. - In general, unless otherwise noted, all object parameters must be stored using the same
num_t
datatype. In a few cases, one of the object parameters must be stored in the real projection of one of the other objects' types. (The real projection of anum_t
datatype is the equivalent datatype in the real domain. SoBLIS_DOUBLE
is the real projection ofBLIS_DCOMPLEX
.BLIS_DOUBLE
is also the real projection of itself.) - Many object API entries list the object properties that are honored/observed by the operation. For example, for
bli_gemv()
, the observed object properties aretrans?(A)
andconj?(x)
. The former means that matrixA
may be (optionally) marked for conjugation and/or tranaposition while the latter means that vectorx
may be (optionally) marked for conjugation. A function may also listdiagoff(A)
as an observe property, which means that it will accept general diagonal offsets. Similarly,diag(A)
refers to recognizing the unit/non-unit structure of the diagonal and anduplo(A)
refers to reading/updating only the stored triangle/trapezoid/region ofA
.
Level-1v operations perform various level-1 BLAS-like operations on vectors (hence the v). Note: Most level-1v operations have a corresponding level-1v kernel through which it is primarily implemented.
void bli_addv
(
obj_t* x,
obj_t* y,
);
Perform
y := y + conj?(x)
where x
and y
are vectors of length n.
Observed object properties: conj?(x)
.
void bli_amaxv
(
obj_t* x,
obj_t* index
);
Given a vector of length n, return the zero-based index of the element of vector x
that contains the largest absolute value (or, in the complex domain, the largest complex modulus). The object index
must be created of type BLIS_INT
.
If NaN
is encountered, it is treated as if it were a valid value that was smaller than any other value in the vector. If more than one element contains the same maximum value, the index of the latter element is returned via index
.
Observed object properties: none.
Note: This function attempts to mimic the algorithm for finding the element with the maximum absolute value in the netlib BLAS routines i?amax()
.
void bli_axpyv
(
obj_t* alpha,
obj_t* x,
obj_t* y
);
Perform
y := y + conj?(alpha) * conj?(x)
where x
and y
are vectors of length n, and alpha
is a scalar.
Observed object properties: conj?(alpha)
, conj?(x)
.
void bli_axpbyv
(
obj_t* alpha,
obj_t* x,
obj_t* beta,
obj_t* y
);
Perform
y := conj?(beta) * y + conj?(alpha) * conj?(x)
where x
and y
are vectors of length n, and alpha
and beta
are scalars.
Observed object properties: conj?(alpha)
, conj?(x)
.
void bli_copyv
(
obj_t* x,
obj_t* y
);
Perform
y := conj?(x)
where x
and y
are vectors of length n.
Observed object properties: conj?(x)
.
void bli_dotv
(
obj_t* x,
obj_t* y,
obj_t* rho
);
Perform
rho := conj?(x)^T * conj?(y)
where x
and y
are vectors of length n, and rho
is a scalar.
Observed object properties: conj?(x)
, conj?(y)
.
void bli_dotxv
(
obj_t* alpha,
obj_t* x,
obj_t* y,
obj_t* beta,
obj_t* rho
);
Perform
rho := conj?(beta) * rho + conj?(alpha) * conj?(x)^T * conj?(y)
where x
and y
are vectors of length n, and alpha
, beta
, and rho
are scalars.
Observed object properties: conj?(alpha)
, conj?(beta)
, conj?(x)
, conj?(y)
.
void bli_invertv
(
obj_t* x
);
Invert all elements of an n-length vector x
.
void bli_invscalv
(
obj_t* alpha,
obj_t* x
);
Perform
x := ( 1.0 / conj?(alpha) ) * x
where x
is a vector of length n, and alpha
is a scalar.
Observed object properties: conj?(alpha)
.
void bli_scalv
(
obj_t* alpha,
obj_t* x
);
Perform
x := conj?(alpha) * x
where x
is a vector of length n, and alpha
is a scalar.
Observed object properties: conj?(alpha)
.
void bli_scal2v
(
obj_t* alpha,
obj_t* x,
obj_t* y
);
Perform
y := conj?(alpha) * conj?(x)
where x
and y
are vectors of length n, and alpha
is a scalar.
Observed object properties: conj?(alpha)
, conj?(x)
.
void bli_setv
(
obj_t* alpha,
obj_t* x
);
Perform
x := conj?(alpha)
That is, set all elements of an n-length vector x
to scalar conj?(alpha)
.
Observed object properties: conj?(alpha)
.
void bli_setrv
(
obj_t* alpha,
obj_t* x
);
Perform
real(x) := real(alpha)
That is, given an n-length vector x
, set all elements' real components to the real component of scalar alpha
. (If alpha
is complex, the imaginary component is ignored.)
If x
is real, this operation is equivalent to performing setv
on x
with the real component of scalar alpha
.
Note: This operation is provided for convenience as an object wrapper to setv
, and thus it has no analogue in the BLIS typed API.
void bli_setiv
(
obj_t* alpha,
obj_t* x
);
Perform
imag(x) := real(alpha)
That is, given an n-length vector x
, set all elements' imaginary components to the real component of scalar alpha
. (If alpha
is complex, the imaginary component is ignored.)
If x
is real, this operation is equivalent to a no-op.
Note: This operation is provided for convenience as an object wrapper to setv
, and thus it has no analogue in the BLIS typed API.
void bli_subv
(
obj_t* x,
obj_t* y
);
Perform
y := y - conj?(x)
where x
and y
are vectors of length n.
Observed object properties: conj?(x)
.
void bli_swapv
(
obj_t* x,
obj_t* y
);
Swap corresponding elements of two n-length vectors x
and y
.
void bli_xpbyv
(
obj_t* x,
obj_t* beta,
obj_t* y
);
Perform
y := conj?(beta) * y + conj?(x)
where x
and y
are vectors of length n, and beta
is a scalar.
Observed object properties: conj?(beta)
, conj?(x)
.
Level-1d operations perform various level-1 BLAS-like operations on matrix diagonals (hence the d).
These operations are similar to their level-1m counterparts, except they only read and update matrix diagonals and therefore ignore the uplo
property of their applicable input operands. Please see the descriptions for the corresponding level-1m operation for a description of the arguments.
void bli_addd
(
obj_t* a,
obj_t* b
);
Observed object properties: diagoff(A)
, diag(A)
, trans?(A)
.
void bli_axpyd
(
obj_t* alpha,
obj_t* a,
obj_t* b
);
Observed object properties: conj?(alpha)
, diagoff(A)
, diag(A)
, trans?(A)
.
void bli_copyd
(
obj_t* a,
obj_t* b
);
Observed object properties: diagoff(A)
, diag(A)
, trans?(A)
.
void bli_invertd
(
obj_t* a
);
Observed object properties: diagoff(A)
.
void bli_invscald
(
obj_t* alpha,
obj_t* a
);
Observed object properties: conj?(alpha)
, diagoff(A)
.
void bli_scald
(
obj_t* alpha,
obj_t* a
);
Observed object properties: conj?(alpha)
, diagoff(A)
.
void bli_scal2d
(
obj_t* alpha,
obj_t* a,
obj_t* b
);
Observed object properties: conj?(alpha)
, diagoff(A)
, diag(A)
, trans?(A)
.
void bli_setd
(
obj_t* alpha,
obj_t* a
);
Observed object properties: conj?(alpha)
, diagoff(A)
.
void bli_setid
(
obj_t* alpha,
obj_t* a
);
Set the imaginary components of every element along the diagonal of a
to a scalar alpha
.
Note that the datatype of alpha
must be the real projection of the datatype
of a
.
Observed object properties: diagoff(A)
.
void bli_shiftd
(
obj_t* alpha,
obj_t* a
);
Add a constant value alpha
to every element along the diagonal of a
.
Observed object properties: diagoff(A)
.
void bli_subd
(
obj_t* a,
obj_t* b
);
Observed object properties: diagoff(A)
, diag(A)
, trans?(A)
.
void bli_xpbyd
(
obj_t* a,
obj_t* beta,
obj_t* b
);
Observed object properties: conj?(beta)
, diagoff(A)
, diag(A)
, trans?(A)
.
Level-1m operations perform various level-1 BLAS-like operations on matrices (hence the m).
void bli_addm
(
obj_t* a,
obj_t* b
);
Perform
B := B + trans?(A)
where B
is an m x n matrix, A
is stored as a dense matrix, or lower- or upper-triangular/trapezoidal matrix with arbitrary diagonal offset and unit or non-unit diagonal.
If uplo(A)
indicates lower or upper storage, only that part of matrix A
will be referenced and used to update B
.
Observed object properties: diagoff(A)
, diag(A)
, uplo(A)
, trans?(A)
.
void bli_axpym
(
obj_t* alpha,
obj_t* a,
obj_t* b
);
Perform
B := B + conj?(alpha) * trans?(A)
where B
is an m x n matrix, A
is stored as a dense matrix, or lower- or upper-triangular/trapezoidal matrix with arbitrary diagonal offset and unit or non-unit diagonal.
If uplo(A)
indicates lower or upper storage, only that part of matrix A
will be referenced and used to update B
.
Observed object properties: conj?(alpha)
, diagoff(A)
, diag(A)
, uplo(A)
, trans?(A)
.
void bli_copym
(
obj_t* a,
obj_t* b
);
Perform
B := trans?(A)
where B
is an m x n matrix, A
is stored as a dense matrix, or lower- or upper-triangular/trapezoidal matrix with arbitrary diagonal offset and unit or non-unit diagonal.
If uplo(A)
indicates lower or upper storage, only that part of matrix A
will be referenced and used to update B
.
Observed object properties: diagoff(A)
, diag(A)
, uplo(A)
, trans?(A)
.
void bli_invscalm
(
obj_t* alpha,
obj_t* a
);
Perform
A := ( 1.0 / conj?(alpha) ) * A
where A
is an m x n matrix stored as a dense matrix, or lower- or upper-triangular/trapezoidal matrix with arbitrary diagonal offset. If uplo(A)
indicates lower or upper storage, only that part of matrix A
will be updated.
Observed object properties: conj?(alpha)
, diagoff(A)
, uplo(A)
.
void bli_scalm
(
obj_t* alpha,
obj_t* a
);
Perform
A := conj?(alpha) * A
where A
is an m x n matrix stored as a dense matrix, or lower- or upper-triangular/trapezoidal matrix with arbitrary diagonal offset. If uplo(A)
indicates lower or upper storage, only that part of matrix A
will be updated.
Observed object properties: conj?(alpha)
, diagoff(A)
, uplo(A)
.
void bli_scal2m
(
obj_t* a,
obj_t* b
);
Perform
B := conj?(alpha) * trans?(A)
where B
is an m x n matrix, A
is stored as a dense matrix, or lower- or upper-triangular/trapezoidal matrix with arbitrary diagonal offset and unit or non-unit diagonal.
If uplo(A)
indicates lower or upper storage, only that part of matrix A
will be referenced and used to update B
.
Observed object properties: conj?(alpha)
, diagoff(A)
, diag(A)
, uplo(A)
, trans?(A)
.
void bli_setm
(
obj_t* alpha,
obj_t* a
);
Perform
A := conj?(alpha)
That is, set all elements of A
to scalar conj?(alpha)
, where A
is an m x n matrix stored as a dense matrix, or lower- or upper-triangular/trapezoidal matrix with arbitrary diagonal offset. If uplo(A)
indicates lower or upper storage, only that part of matrix A
will be updated.
Observed object properties: conj?(alpha)
, diagoff(A)
, diag(A)
, uplo(A)
.
void bli_setrm
(
obj_t* alpha,
obj_t* a
);
Perform
real(A) := real(alpha)
That is, given an m x n matrix A
, set all elements' real components to the real component of scalar alpha
. (If alpha
is complex, the imaginary component is ignored.)
If A
is real, this operation is equivalent to performing setm
on A
with the real component of scalar alpha
.
Note: This operation is provided for convenience as an object wrapper to setm
, and thus it has no analogue in the BLIS typed API.
Observed object properties: diagoff(A)
, diag(A)
, uplo(A)
.
void bli_setim
(
obj_t* alpha,
obj_t* a
);
Perform
imag(A) := real(alpha)
That is, given an m x n matrix A
, set all elements' imaginary components to the real component of scalar alpha
. (If alpha
is complex, the imaginary component is ignored.)
If A
is real, this operation is equivalent to a no-op.
Note: This operation is provided for convenience as an object wrapper to setm
, and thus it has no analogue in the BLIS typed API.
Observed object properties: diagoff(A)
, diag(A)
, uplo(A)
.
void bli_subm
(
obj_t* a,
obj_t* b
);
Perform
B := B - trans?(A)
where B
is an m x n matrix, A
is stored as a dense matrix, or lower- or upper-triangular/trapezoidal matrix with arbitrary diagonal offset and unit or non-unit diagonal.
If uplo(A)
indicates lower or upper storage, only that part of matrix A
will be referenced and used to update B
.
Observed object properties: diagoff(A)
, diag(A)
, uplo(A)
, trans?(A)
.
Level-1f operations implement various fused combinations of level-1 operations (hence the f). Note: Each level-1f operation has a corresponding level-1f kernel through which it is primarily implemented.
Level-1f kernels are employed when optimizing level-2 operations.
void bli_axpy2v
(
obj_t* alphax,
obj_t* alphay,
obj_t* x,
obj_t* y,
obj_t* z
);
Perform
y := y + conj?(alphax) * conj?(x) + conj?(alphay) * conj?(y)
where x
, y
, and z
are vectors of length m. The kernel, if optimized, is implemented as a fused pair of calls to axpyv.
Observed object properties: conj?(alphax)
, conj?(x)
, conj?(alphay)
, conj?(y)
.
void bli_dotaxpyv
(
obj_t* alpha,
obj_t* x,
obj_t* y,
obj_t* rho,
obj_t* z
);
Perform
rho := conj?(x)^T * conj?(y)
y := y + conj?(alpha) * conj?(x)
where x
, y
, and z
are vectors of length m and alpha
and rho
are scalars. The kernel, if optimized, is implemented as a fusion of calls to dotv and axpyv.
Observed object properties: conj?(x)
, conj?(y)
, conj?(alpha)
.
void bli_axpyf
(
obj_t* alpha,
obj_t* a,
obj_t* x,
obj_t* y
);
Perform
y := y + alpha * conja(A) * conjx(x)
where A
is an m x b matrix, and x
and y
are vectors. The kernel, if optimized, is implemented as a fused series of calls to axpyv where b is less than or equal to an implementation-dependent fusing factor specific to axpyf
.
Observed object properties: conj?(alpha)
, conj?(A)
, conj?(x)
.
void bli_dotxf
(
obj_t* alpha,
obj_t* a,
obj_t* x,
obj_t* beta,
obj_t* y
);
Perform
y := conj?(beta) * y + conj?(alpha) * conj?(A)^T * conj?(x)
where A
is an m x b matrix, and x
and y
are vectors. The kernel, if optimized, is implemented as a fused series of calls to dotxv where b is less than or equal to an implementation-dependent fusing factor specific to dotxf
.
Observed object properties: conj?(alpha)
, conj?(beta)
, conj?(A)
, conj?(x)
.
void bli_dotxaxpyf
(
obj_t* alpha,
obj_t* a,
obj_t* w,
obj_t* x,
obj_t* beta,
obj_t* y,
obj_t* z
);
Perform
y := conj?(beta) * y + conj?(alpha) * conj?(A)^T * conj?(w)
z := z + conj?(alpha) * conj?(A) * conj?(x)
where A
is an m x b matrix, w
and z
are vectors of length m, x
and y
are vectors of length b
, and alpha
and beta
are scalars. The kernel, if optimized, is implemented as a fusion of calls to dotxf and axpyf.
Observed object properties: conj?(alpha)
, conj?(beta)
, conj?(A)
, conj?(w)
, conj?(x)
.
Level-2 operations perform various level-2 BLAS-like operations.
void bli_gemv
(
obj_t* alpha,
obj_t* a,
obj_t* x,
obj_t* beta,
obj_t* y
);
Perform
y := conj?(beta) * y + conj?(alpha) * trans?(A) * conj?(x)
where trans?(A)
is an m x n matrix, and x
and y
are vectors.
Observed object properties: conj?(alpha)
, conj?(beta)
, trans?(A)
, conj?(x)
.
void bli_ger
(
obj_t* alpha,
obj_t* x,
obj_t* y,
obj_t* a
);
Perform
A := A + conj?(alpha) * conj?(x) * conj?(y)^T
where A
is an m x n matrix, and x
and y
are vectors of length m and n, respectively.
Observed object properties: conj?(alpha)
, conj?(x)
, conj?(y)
.
void bli_hemv
(
obj_t* alpha,
obj_t* a,
obj_t* x,
obj_t* beta,
obj_t* y
);
Perform
y := conj?(beta) * y + conj?(alpha) * conj?(A) * conj?(x)
where A
is an m x m Hermitian matrix stored in the lower or upper triangle as specified by uplo(A)
, and x
and y
are vectors of length m.
Observed object properties: conj?(alpha)
, conj?(beta)
, conj?(A)
, uplo(A)
, conj?(x)
.
void bli_her
(
obj_t* alpha,
obj_t* x,
obj_t* a
);
Perform
A := A + conj?(alpha) * conj?(x) * conj?(x)^H
where A
is an m x m Hermitian matrix stored in the lower or upper triangle as specified by uplo(A)
, and x
is a vector of length m.
Observed object properties: conj?(alpha)
, uplo(A)
, conj?(x)
.
Note: The floating-point (num_t
) type of alpha
is always the real projection of the floating-point types of x
and A
.
void bli_her2
(
obj_t* alpha,
obj_t* x,
obj_t* y,
obj_t* a
);
Perform
A := A + alpha * conj?(x) * conj?(y)^H + conj(alpha) * conj?(y) * conj?(x)^H
where A
is an m x m Hermitian matrix stored in the lower or upper triangle as specified by uplo(A)
, and x
and y
are vectors of length m.
Observed object properties: uplo(A)
, conj?(x)
, conj?(y)
.
void bli_symv
(
obj_t* alpha,
obj_t* a,
obj_t* x,
obj_t* beta,
obj_t* y
);
Perform
y := conj?(beta) * y + conj?(alpha) * conj?(A) * conj?(x)
where A
is an m x m symmetric matrix stored in the lower or upper triangle as specified by uplo(A)
, and x
and y
are vectors of length m.
Observed object properties: conj?(alpha)
, conj?(beta)
, conj?(A)
, uplo(A)
, conj?(x)
.
void bli_syr
(
obj_t* alpha,
obj_t* x,
obj_t* a
);
Perform
A := A + conj?(alpha) * conj?(x) * conj?(x)^T
where A
is an m x m symmetric matrix stored in the lower or upper triangle as specified by uploa
, and x
is a vector of length m.
Observed object properties: conj?(alpha)
, conj?(x)
.
void bli_syr2
(
obj_t* alpha,
obj_t* x,
obj_t* y,
obj_t* a
);
Perform
A := A + alpha * conj?(x) * conj?(y)^T + conj(alpha) * conj?(y) * conj?(x)^T
where A
is an m x m symmetric matrix stored in the lower or upper triangle as specified by uplo(A)
, and x
and y
are vectors of length m.
Observed object properties: uplo(A)
, conj?(x)
, conj?(y)
.
void bli_trmv
(
obj_t* alpha,
obj_t* a,
obj_t* x
);
Perform
x := conj?(alpha) * transa(A) * x
where A
is an m x m triangular matrix stored in the lower or upper triangle as specified by uplo(A)
with unit/non-unit nature specified by diag(A)
, and x
is a vector of length m.
Observed object properties: conj?(alpha)
, uplo(A)
, trans?(A)
, diag(A)
.
void bli_trsv
(
obj_t* alpha,
obj_t* a,
obj_t* y
);
Solve the linear system
transa(A) * x = alpha * y
where A
is an m x m triangular matrix stored in the lower or upper triangle as specified by uplo(A)
with unit/non-unit nature specified by diag(A)
, and x
and y
are vectors of length m. The right-hand side vector operand y
is overwritten with the solution vector x
.
Observed object properties: conj?(alpha)
, uplo(A)
, trans?(A)
, diag(A)
.
Level-3 operations perform various level-3 BLAS-like operations. Note: Each All level-3 operations are implemented through a handful of level-3 microkernels. Please see the Kernels Guide for more details.
void bli_gemm
(
obj_t* alpha,
obj_t* a,
obj_t* b,
obj_t* beta,
obj_t* c
);
Perform
C := beta * C + alpha * trans?(A) * trans?(B)
where C
is an m x n matrix, trans?(A)
is an m x k matrix, and trans?(B)
is a k x n matrix.
Observed object properties: trans?(A)
, trans?(B)
.
void bli_gemmt
(
obj_t* alpha,
obj_t* a,
obj_t* b,
obj_t* beta,
obj_t* c
);
Perform
C := beta * C + alpha * trans?(A) * trans?(B)
where C
is an m x m matrix, trans?(A)
is an m x k matrix, and trans?(B)
is a k x m matrix. This operation is similar to bli_gemm()
except that it only updates the lower or upper triangle of C
as specified by uplo(C)
.
Observed object properties: trans?(A)
, trans?(B)
, uplo(C)
.
void bli_hemm
(
side_t sidea,
obj_t* alpha,
obj_t* a,
obj_t* b,
obj_t* beta,
obj_t* c
);
Perform
C := beta * C + alpha * conj?(A) * trans?(B)
if sidea
is BLIS_LEFT
, or
C := beta * C + alpha * trans?(B) * conj?(A)
if sidea
is BLIS_RIGHT
, where C
and B
are m x n matrices and A
is a Hermitian matrix stored in the lower or upper triangle as specified by uplo(A)
. When sidea
is BLIS_LEFT
, A
is m x m, and when sidea
is BLIS_RIGHT
, A
is n x n.
Observed object properties: uplo(A)
, conj?(A)
, trans?(B)
.
void bli_herk
(
obj_t* alpha,
obj_t* a,
obj_t* beta,
obj_t* c
);
Perform
C := beta * C + alpha * trans?(A) * trans?(A)^H
where C
is an m x m Hermitian matrix stored in the lower or upper triangle as specified by uplo(C)
and trans?(A)
is an m x k matrix.
Observed object properties: trans?(A)
, uplo(C)
.
Note: The floating-point (num_t
) types of alpha
and beta
are always the real projection of the floating-point types of A
and C
.
void bli_her2k
(
obj_t* alpha,
obj_t* a,
obj_t* b,
obj_t* beta,
obj_t* c
);
Perform
C := beta * C + alpha * trans?(A) * trans?(B)^H + conj(alpha) * trans?(B) * trans?(A)^H
where C
is an m x m Hermitian matrix stored in the lower or upper triangle as specified by uplo(C)
and trans?(A)
and trans?(B)
are m x k matrices.
Observed object properties: trans?(A)
, trans?(B)
, uplo(C)
.
Note: The floating-point (num_t
) type of beta
is always the real projection of the floating-point types of A
and C
.
void bli_symm
(
side_t sidea,
obj_t* alpha,
obj_t* a,
obj_t* b,
obj_t* beta,
obj_t* c
);
Perform
C := beta * C + alpha * conj?(A) * trans?(B)
if sidea
is BLIS_LEFT
, or
C := beta * C + alpha * trans?(B) * conj?(A)
if sidea
is BLIS_RIGHT
, where C
and B
are m x n matrices and A
is a symmetric matrix stored in the lower or upper triangle as specified by uplo(A)
. When sidea
is BLIS_LEFT
, A
is m x m, and when sidea
is BLIS_RIGHT
, A
is n x n.
Observed object properties: uplo(A)
, conj?(A)
, trans?(B)
.
void bli_syrk
(
obj_t* alpha,
obj_t* a,
obj_t* beta,
obj_t* c
);
Perform
C := beta * C + alpha * trans?(A) * trans?(A)^T
where C
is an m x m symmetric matrix stored in the lower or upper triangle as specified by uplo(A)
and trans?(A)
is an m x k matrix.
Observed object properties: trans?(A)
, uplo(C)
.
void bli_syr2k
(
obj_t* alpha,
obj_t* a,
obj_t* b,
obj_t* beta,
obj_t* c
);
Perform
C := beta * C + alpha * trans?(A) * trans?(B)^T + alpha * trans?(B) * trans?(A)^T
where C
is an m x m symmetric matrix stored in the lower or upper triangle as specified by uplo(A)
and trans?(A)
and trans?(B)
are m x k matrices.
Observed object properties: trans?(A)
, trans?(B)
, uplo(C)
.
void bli_trmm
(
side_t sidea,
obj_t* alpha,
obj_t* a,
obj_t* b
);
Perform
B := alpha * transa(A) * B
if sidea
is BLIS_LEFT
, or
B := alpha * B * transa(A)
if sidea
is BLIS_RIGHT
, where B
is an m x n matrix and A
is a triangular matrix stored in the lower or upper triangle as specified by uplo(A)
with unit/non-unit nature specified by diag(A)
. When sidea
is BLIS_LEFT
, A
is m x m, and when sidea
is BLIS_RIGHT
, A
is n x n.
Observed object properties: uplo(A)
, trans?(A)
, diag(A)
.
void bli_trmm3
(
side_t sidea,
obj_t* alpha,
obj_t* a,
obj_t* b,
obj_t* beta,
obj_t* c
);
Perform
C := beta * C + alpha * trans?(A) * trans?(B)
if sidea
is BLIS_LEFT
, or
C := beta * C + alpha * trans?(B) * trans?(A)
if sidea
is BLIS_RIGHT
, where C
and trans?(B)
are m x n matrices and A
is a triangular matrix stored in the lower or upper triangle as specified by uplo(A)
with unit/non-unit nature specified by diag(A)
. When sidea
is BLIS_LEFT
, A
is m x m, and when sidea
is BLIS_RIGHT
, A
is n x n.
Observed object properties: uplo(A)
, trans?(A)
, diag(A)
, trans?(B)
.
void bli_trsm
(
side_t sidea,
obj_t* alpha,
obj_t* a,
obj_t* b
);
Solve the linear system with multiple right-hand sides
transa(A) * X = alpha * B
if sidea
is BLIS_LEFT
, or
X * transa(A) = alpha * B
if sidea
is BLIS_RIGHT
, where X
and B
are an m x n matrices and A
is a triangular matrix stored in the lower or upper triangle as specified by uplo(A)
with unit/non-unit nature specified by diag(A)
. When sidea
is BLIS_LEFT
, A
is m x m, and when sidea
is BLIS_RIGHT
, A
is n x n. The right-hand side matrix operand B
is overwritten with the solution matrix X
.
Observed object properties: uplo(A)
, trans?(A)
, diag(A)
.
void bli_asumv
(
obj_t* x,
obj_t* asum
);
Compute the sum of the absolute values of the fundamental elements of vector x
. The resulting sum is stored to asum
.
Observed object properties: none.
Note: The floating-point type of asum
is always the real projection of the floating-point type of x
.
Note: This function attempts to mimic the algorithm for computing the absolute vector sum in the netlib BLAS routines *asum()
.
void bli_norm[1fi]m
(
obj_t* a,
obj_t* norm
);
Compute the one-norm (bli_norm1m()
), Frobenius norm (bli_normfm()
), or infinity norm (bli_normim()
) of the elements in an m x n matrix A
. If uplo(A)
is BLIS_LOWER
or BLIS_UPPER
then A
is assumed to be lower or upper triangular, respectively, with the main diagonal located at offset diagoff(A)
. The resulting norm is stored to norm
.
Observed object properties: diagoff(A)
, diag(A)
, uplo(A)
.
Note: The floating-point (num_t
) type of norm
is always the real projection of the floating-point type of x
.
void bli_norm[1fi]v
(
obj_t* x,
obj_t* norm
);
Compute the one-norm (bli_norm1v()
), Frobenius norm (bli_normfv()
), or infinity norm (bli_normiv()
) of the elements in a vector x
of length n. The resulting norm is stored to norm
.
Observed object properties: diagoff(A)
, diag(A)
, uplo(A)
.
Note: The floating-point (num_t
) type of norm
is always the real projection of the floating-point type of x
.
void bli_mkherm
(
obj_t* a
);
Make an m x m matrix A
explicitly Hermitian by copying the conjugate of the triangle specified by uplo(A)
to the opposite triangle. Imaginary components of diagonal elements are explicitly set to zero. It is assumed that the diagonal offset of A
is zero.
Observed object properties: uplo(A)
.
void bli_mksymm
(
obj_t* a
);
Make an m x m matrix A
explicitly symmetric by copying the triangle specified by uplo(A)
to the opposite triangle. It is assumed that the diagonal offset of A
is zero.
Observed object properties: uplo(A)
.
void bli_mktrim
(
obj_t* a
);
Make an m x m matrix A
explicitly triangular by preserving the triangle specified by uplo(A)
and zeroing the elements in the opposite triangle. It is assumed that the diagonal offset of A
is zero.
Observed object properties: uplo(A)
.
void bli_fprintv
(
FILE* file,
char* s1,
obj_t* x,
char* format,
char* s2
);
Print a vector x
of length m to file stream file
, where file
is a file pointer returned by the standard C library function fopen()
. The caller may also pass in a global file pointer such as stdout
or stderr
. The strings s1
and s2
are printed immediately before and after the output (respectively), and the format specifier format
is used to format the individual elements. For valid format specifiers, please see documentation for the standard C library function printf()
.
Note: For complex datatypes, the format specifier is applied to both the real and imaginary components individually. Therefore, you should use format specifiers such as "%5.2f"
, but not "%5.2f + %5.2f"
.
void bli_fprintm
(
FILE* file,
char* s1,
obj_t* a,
char* format,
char* s2
);
Print an m x n matrix A
to file stream file
, where file
is a file pointer returned by the standard C library function fopen()
. The caller may also pass in a global file pointer such as stdout
or stderr
. The strings s1
and s2
are printed immediately before and after the output (respectively), and the format specifier format
is used to format the individual elements. For valid format specifiers, please see documentation for the standard C library function printf()
.
Note: For complex datatypes, the format specifier is applied to both the real and imaginary components individually. Therefore, you should use format specifiers such as "%5.2f"
, but not "%5.2f + %5.2f"
.
void bli_printv
(
char* s1,
obj_t* x,
char* format,
char* s2
);
Print a vector x
of length m to standard output. This function call is equivalent to calling bli_fprintv()
with stdout
as the file pointer.
void bli_printm
(
char* s1,
obj_t* a,
char* format,
char* s2
);
Print an m x n matrix a
to standard output. This function call is equivalent to calling bli_fprintm()
with stdout
as the file pointer.
void bli_randv
(
obj_t* x
);
Set the elements of a vector x
of length n to random values on the interval [-1,1)
.
Note: For complex datatypes, the real and imaginary components of each element are randomized individually and independently of one another.
void bli_randm
(
obj_t* a
);
Set the elements of an m x n matrix A
to random values on the interval [-1,1)
. Off-diagonal elements (in the triangle specified by uplo(A)
) are scaled by 1.0/max(m,n)
.
Observed object properties: diagoff(A)
, uplo(A)
.
Note: For complex datatypes, the real and imaginary components of each off-diagonal element are randomized individually and independently of one another.
Note: If uplo(A)
is BLIS_LOWER
or BLIS_UPPER
and you plan to use this matrix to test trsv
or trsm
, additional scaling of the diagonal is recommended to ensure that the matrix is invertible. In this case, try using the addd operation to increase the magnitude to the diagonal elements.
void bli_sumsqv
(
obj_t* x,
obj_t* scale,
obj_t* sumsq
);
Compute the sum of the squares of the elements in a vector x
of length n. The result is computed in scaled form, and in such a way that it may be used repeatedly to accumulate the sum of the squares of several vectors.
The function computes scale_new and sumsq_new such that
scale_new^2 * sumsq_new = x[0]^2 + x[1]^2 + ... x[m-1]^2 + scale_old^2 * sumsq_old
where, on entry, scale
and sumsq
contain scale_old
and sumsq_old
, respectively, and on exit, scale
and sumsq
contain scale_new
and sumsq_new
, respectively.
Note: This function attempts to mimic the algorithm for computing the Frobenius norm in the netlib LAPACK routine ?lassq()
.
Note: The floating-point (num_t
) types of scale
and sumsq
are always the real projection of the floating-point type of x
.
void bli_getsc
(
obj_t* chi,
double* zeta_r,
double* zeta_i
);
Copy the real and imaginary values from the scalar object chi
to zeta_r
and zeta_i
. If chi
is stored as a real type, then zeta_i
is set to zero. (If chi
is stored in single precision, the corresponding elements are typecast/promoted during the copy.)
err_t bli_getijv
(
dim_t i,
obj_t* b,
double* ar,
double* ai
)
Copy the real and imaginary values at the i
th element of vector object x
to ar
and ai
. If elements of x
are stored as real types, then only ar
is overwritten and ai
is left unchanged. (If x
contains elements stored in single precision, the corresponding elements are typecast/promoted during the copy.)
If either the element offset i
is beyond the vector dimension of x
or less than zero, the function returns BLIS_FAILURE
without taking any action. Similarly, if x
is a global scalar constant such as BLIS_ONE
, the function returns BLIS_FAILURE
.
err_t bli_getijm
(
dim_t i,
dim_t j,
obj_t* b,
double* ar,
double* ai
)
Copy the real and imaginary values at the (i
,j
) element of object b
to ar
and ai
. If elements of b
are stored as real types, then only ar
is overwritten and ai
is left unchanged. (If b
contains elements stored in single precision, the corresponding elements are typecast/promoted during the copy.)
If either the row offset i
is beyond the m dimension of b
or less than zero, or column offset j
is beyond the n dimension of b
or less than zero, the function returns BLIS_FAILURE
without taking any action. Similarly, if b
is a global scalar constant such as BLIS_ONE
, the function returns BLIS_FAILURE
.
void bli_setsc
(
double* zeta_r,
double* zeta_i,
obj_t* chi
);
Copy real and imaginary values zeta_r
and zeta_i
to the scalar object chi
. If chi
is stored as a real type, then zeta_i
is ignored. (If chi
is stored in single precision, the contents are typecast/demoted during the copy.)
err_t bli_setijv
(
double ar,
double ai,
dim_t i,
obj_t* x
);
Copy real and imaginary values ar
and ai
to the i
th element of vector object x
. If elements of x
are stored as real types, then only ar
is copied and ai
is ignored. (If x
contains elements stored in single precision, the corresponding elements are typecast/demoted during the copy.)
If the element offset i
is beyond the vector dimension of x
or less than zero, the function returns BLIS_FAILURE
without taking any action. Similarly, if x
is a global scalar constant such as BLIS_ONE
, the function returns BLIS_FAILURE
.
err_t bli_setijm
(
double ar,
double ai,
dim_t i,
dim_t j,
obj_t* b
);
Copy real and imaginary values ar
and ai
to the (i
,j
) element of object b
. If elements of b
are stored as real types, then only ar
is copied and ai
is ignored. (If b
contains elements stored in single precision, the corresponding elements are typecast/demoted during the copy.)
If either the row offset i
is beyond the m dimension of b
or less than zero, or column offset j
is beyond the n dimension of b
or less than zero, the function returns BLIS_FAILURE
without taking any action. Similarly, if b
is a global scalar constant such as BLIS_ONE
, the function returns BLIS_FAILURE
.
void bli_eqsc
(
obj_t chi,
obj_t psi,
bool* is_eq
);
Perform an element-wise comparison between scalars chi
and psi
and store the boolean result in the bool
pointed to by is_eq
.
If exactly one of conj(chi)
or conj(psi)
(but not both) indicate a conjugation, then one of the scalars will be implicitly conjugated for purposes of the comparision.
Observed object properties: conj?(chi)
, conj?(psi)
.
void bli_eqv
(
obj_t x,
obj_t y,
bool* is_eq
);
Perform an element-wise comparison between vectors x
and y
and store the boolean result in the bool
pointed to by is_eq
.
If exactly one of conj(x)
or conj(y)
(but not both) indicate a conjugation, then one of the vectors will be implicitly conjugated for purposes of the comparision.
Observed object properties: conj?(x)
, conj?(y)
.
void bli_eqm
(
obj_t a,
obj_t b,
bool* is_eq
);
Perform an element-wise comparison between matrices A
and B
and store the boolean result in the bool
pointed to by is_eq
.
Here, A
is stored as a dense matrix, or lower- or upper-triangular/trapezoidal matrix with arbitrary diagonal offset and unit or non-unit diagonal.
If diag(A)
indicates a unit diagonal, the diagonals of both matrices will be ignored for purposes of the comparision.
If uplo(A)
indicates lower or upper storage, only that part of both matrices A
and B
will be referenced.
If exactly one of trans(A)
or trans(B)
(but not both) indicate a transposition, then one of the matrices will be transposed for purposes of the comparison.
Similarly, if exactly one of trans(A)
or trans(B)
(but not both) indicate a conjugation, then one of the matrices will be implicitly conjugated for purposes of the comparision.
Observed object properties: diagoff(A)
, diag(A)
, uplo(A)
, trans?(A)
, trans?(B)
.
BLIS allows applications to query information about how BLIS was configured. The bli_info_
API provides several categories of query routines. Most values are returned as a gint_t
, which is a signed integer. The size of this integer can be queried through a special routine that returns the size in a character string:
char* bli_info_get_int_type_size_str( void );
Note: All of the bli_info_
functions are always thread-safe, no matter how BLIS was configured.
The following routine returns the address the full BLIS version string:
char* bli_info_get_version_str( void );
The following routine returns a unique ID of type arch_t
that identifies the current current active configuration:
arch_t bli_arch_query_id( void );
This is most useful when BLIS is configured with multiple configurations. (When linking to multi-configuration builds of BLIS, you don't know for sure which configuration will be used until runtime since the configuration-specific parameters are not loaded until after calling a hueristic to detect the hardware--usually based the CPUID
instruction.)
Once the configuration's ID is known, it can be used to query a string that contains the name of the configuration:
char* bli_arch_string( arch_t id );
The following routines return various general-purpose constants that affect the entire framework. All of these settings default to sane values, which can then be overridden by the configuration in bli_config.h. If they are absent from a particular configuration's bli_config.h
header file, then the default value is used, as specified in frame/include/bli_config_macro_defs.h.
gint_t bli_info_get_int_type_size( void );
gint_t bli_info_get_num_fp_types( void );
gint_t bli_info_get_max_type_size( void );
gint_t bli_info_get_page_size( void );
gint_t bli_info_get_simd_num_registers( void );
gint_t bli_info_get_simd_size( void );
gint_t bli_info_get_simd_align_size( void );
gint_t bli_info_get_stack_buf_max_size( void );
gint_t bli_info_get_stack_buf_align_size( void );
gint_t bli_info_get_heap_addr_align_size( void );
gint_t bli_info_get_heap_stride_align_size( void );
gint_t bli_info_get_pool_addr_align_size( void );
gint_t bli_info_get_enable_stay_auto_init( void );
gint_t bli_info_get_enable_blas( void );
gint_t bli_info_get_blas_int_type_size( void );
The following routines allow the caller to obtain a string that identifies the implementation type of each microkernel that is currently active (ie: part of the current active configuration, as identified bi bli_arch_query_id()
).
char* bli_info_get_gemm_ukr_impl_string( ind_t method, num_t dt )
char* bli_info_get_gemmtrsm_l_ukr_impl_string( ind_t method, num_t dt )
char* bli_info_get_gemmtrsm_u_ukr_impl_string( ind_t method, num_t dt )
char* bli_info_get_trsm_l_ukr_impl_string( ind_t method, num_t dt )
char* bli_info_get_trsm_u_ukr_impl_string( ind_t method, num_t dt )
Possible implementation (ie: the ind_t method
argument) types are:
BLIS_1M
: Implementation based on the 1m method. (This is the default induced method when real domain kernels are present but complex kernels are missing.)BLIS_NAT
: Implementation based on "native" execution (ie: NOT an induced method).
Possible microkernel types (ie: the return values for bli_info_get_*_ukr_impl_string()
) are:
BLIS_REFERENCE_UKERNEL
("refrnce"
): This value is returned when the queried microkernel is provided by the reference implementation.BLIS_VIRTUAL_UKERNEL
("virtual"
): This value is returned when the queried microkernel is driven by a the "virtual" microkernel provided by an induced method. This happens for anymethod
value that is notBLIS_NAT
(ie: native), but only applies to the complex domain.BLIS_OPTIMIZED_UKERNEL
("optimzd"
): This value is returned when the queried microkernel is provided by an implementation that is neither reference nor virtual, and thus we assume the kernel author would deem it to be "optimized". Such a microkernel may not be optimal in the literal sense of the word, but nonetheless is intended to be optimized, at least relative to the reference microkernels.BLIS_NOTAPPLIC_UKERNEL
("notappl"
): This value is returned usually when performing agemmtrsm
ortrsm
microkernel type query for anymethod
value that is notBLIS_NAT
(ie: native). That is, induced methods cannot be (purely) used ontrsm
-based microkernels because these microkernels perform more a triangular inversion, which is not matrix multiplication.
double bli_clock
(
void
);
Return the amount of time that has elapsed since some fixed time in the past. The return values of bli_clock()
typically feature nanosecond precision, though this is not guaranteed.
Note: On Linux, bli_clock()
is implemented in terms of clock_gettime()
using the clockid_t
value of CLOCK_MONOTONIC
. On OS X, bli_clock
is implemented in terms of mach_absolute_time()
. And on Windows, bli_clock
is implemented in terms of QueryPerformanceFrequency()
. Please see frame/base/bli_clock.c for more details.
Note: This function is returns meaningless values when BLIS is configured with --disable-system
.
double bli_clock_min_diff
(
double time_prev_min,
double time_start
);
This function computes an intermediate value, time_diff
, equal to bli_clock() - time_start
, and then tentatively prepares to return the minimum value of time_diff
and time_min
. If that minimum value is extremely small (close to zero), the function returns time_min
instead.
This function is meant to be used in conjuction with bli_clock()
for
performance timing within applications--specifically in loops where only
the fastest timing is of interest. For example:
double t_save = DBL_MAX;
for( i = 0; i < 3; ++i )
{
double t = bli_clock();
bli_gemm( ... );
t_save = bli_clock_min_diff( t_save, t );
}
double gflops = ( 2.0 * m * k * n ) / ( t_save * 1.0e9 );
This code calls bli_gemm()
three times and computes the performance, in GFLOPS, of the fastest of the three executions.
BLIS provides lots of example code in the examples/oapi directory of the BLIS source distribution. The example code in this directory is set up like a tutorial, and so we recommend starting from the beginning. Topics include creating and managing objects, printing vectors and matrices, setting and querying object properties, and calling a representative subset of the computational level-1v, -1m, -2, -3, and utility operations documented above. Please read the README
contained within the examples/oapi
directory for further details.