#include <ceed-impl.h>
#include <ceed.h>
#include <ceed/backend.h>
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
static struct CeedVector_private ceed_vector_active;
static struct CeedVector_private ceed_vector_none;
const CeedVector CEED_VECTOR_ACTIVE = &ceed_vector_active;
const CeedVector CEED_VECTOR_NONE = &ceed_vector_none;
int CeedVectorHasValidArray(CeedVector vec, bool *has_valid_array) {
CeedCheck(vec->HasValidArray, vec->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support HasValidArray");
if (vec->length == 0) {
*has_valid_array = true;
return CEED_ERROR_SUCCESS;
}
CeedCall(vec->HasValidArray(vec, has_valid_array));
return CEED_ERROR_SUCCESS;
}
int CeedVectorHasBorrowedArrayOfType(CeedVector vec, CeedMemType mem_type, bool *has_borrowed_array_of_type) {
CeedCheck(vec->HasBorrowedArrayOfType, vec->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support HasBorrowedArrayOfType");
CeedCall(vec->HasBorrowedArrayOfType(vec, mem_type, has_borrowed_array_of_type));
return CEED_ERROR_SUCCESS;
}
int CeedVectorGetState(CeedVector vec, uint64_t *state) {
*state = vec->state;
return CEED_ERROR_SUCCESS;
}
int CeedVectorGetData(CeedVector vec, void *data) {
*(void **)data = vec->data;
return CEED_ERROR_SUCCESS;
}
int CeedVectorSetData(CeedVector vec, void *data) {
vec->data = data;
return CEED_ERROR_SUCCESS;
}
int CeedVectorReference(CeedVector vec) {
vec->ref_count++;
return CEED_ERROR_SUCCESS;
}
int CeedVectorCreate(Ceed ceed, CeedSize length, CeedVector *vec) {
if (!ceed->VectorCreate) {
Ceed delegate;
CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector"));
CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support VectorCreate");
CeedCall(CeedVectorCreate(delegate, length, vec));
return CEED_ERROR_SUCCESS;
}
CeedCall(CeedCalloc(1, vec));
CeedCall(CeedReferenceCopy(ceed, &(*vec)->ceed));
(*vec)->ref_count = 1;
(*vec)->length = length;
(*vec)->state = 0;
CeedCall(ceed->VectorCreate(length, *vec));
return CEED_ERROR_SUCCESS;
}
int CeedVectorReferenceCopy(CeedVector vec, CeedVector *vec_copy) {
if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) CeedCall(CeedVectorReference(vec));
CeedCall(CeedVectorDestroy(vec_copy));
*vec_copy = vec;
return CEED_ERROR_SUCCESS;
}
int CeedVectorCopy(CeedVector vec, CeedVector vec_copy) {
Ceed ceed;
CeedMemType mem_type, mem_type_copy;
CeedScalar *array;
CeedVectorGetCeed(vec, &ceed);
CeedGetPreferredMemType(ceed, &mem_type);
CeedVectorGetCeed(vec_copy, &ceed);
CeedGetPreferredMemType(ceed, &mem_type_copy);
if (mem_type != mem_type_copy) mem_type = CEED_MEM_HOST;
CeedCall(CeedVectorGetArray(vec, mem_type, &array));
CeedCall(CeedVectorSetArray(vec_copy, mem_type, CEED_COPY_VALUES, array));
CeedCall(CeedVectorRestoreArray(vec, &array));
return CEED_ERROR_SUCCESS;
}
int CeedVectorSetArray(CeedVector vec, CeedMemType mem_type, CeedCopyMode copy_mode, CeedScalar *array) {
CeedCheck(vec->SetArray, vec->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support VectorSetArray");
CeedCheck(vec->state % 2 == 0, vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, the access lock is already in use");
CeedCheck(vec->num_readers == 0, vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, a process has read access");
if (vec->length > 0) CeedCall(vec->SetArray(vec, mem_type, copy_mode, array));
vec->state += 2;
return CEED_ERROR_SUCCESS;
}
int CeedVectorSetValue(CeedVector vec, CeedScalar value) {
CeedCheck(vec->state % 2 == 0, vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, the access lock is already in use");
CeedCheck(vec->num_readers == 0, vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, a process has read access");
if (vec->SetValue) {
CeedCall(vec->SetValue(vec, value));
} else {
CeedScalar *array;
CeedCall(CeedVectorGetArrayWrite(vec, CEED_MEM_HOST, &array));
for (CeedSize i = 0; i < vec->length; i++) array[i] = value;
CeedCall(CeedVectorRestoreArray(vec, &array));
}
vec->state += 2;
return CEED_ERROR_SUCCESS;
}
int CeedVectorSyncArray(CeedVector vec, CeedMemType mem_type) {
CeedCheck(vec->state % 2 == 0, vec->ceed, CEED_ERROR_ACCESS, "Cannot sync CeedVector, the access lock is already in use");
if (vec->length == 0) return CEED_ERROR_SUCCESS;
if (vec->SyncArray) {
CeedCall(vec->SyncArray(vec, mem_type));
} else {
const CeedScalar *array;
CeedCall(CeedVectorGetArrayRead(vec, mem_type, &array));
CeedCall(CeedVectorRestoreArrayRead(vec, &array));
}
return CEED_ERROR_SUCCESS;
}
int CeedVectorTakeArray(CeedVector vec, CeedMemType mem_type, CeedScalar **array) {
CeedScalar *temp_array = NULL;
CeedCheck(vec->state % 2 == 0, vec->ceed, CEED_ERROR_ACCESS, "Cannot take CeedVector array, the access lock is already in use");
CeedCheck(vec->num_readers == 0, vec->ceed, CEED_ERROR_ACCESS, "Cannot take CeedVector array, a process has read access");
if (vec->length > 0) {
bool has_borrowed_array_of_type = true;
CeedCall(CeedVectorHasBorrowedArrayOfType(vec, mem_type, &has_borrowed_array_of_type));
CeedCheck(has_borrowed_array_of_type, vec->ceed, CEED_ERROR_BACKEND,
"CeedVector has no borrowed %s array, must set array with CeedVectorSetArray", CeedMemTypes[mem_type]);
bool has_valid_array = true;
CeedCall(CeedVectorHasValidArray(vec, &has_valid_array));
CeedCheck(has_valid_array, vec->ceed, CEED_ERROR_BACKEND,
"CeedVector has no valid data to take, must set data with CeedVectorSetValue or CeedVectorSetArray");
CeedCall(vec->TakeArray(vec, mem_type, &temp_array));
}
if (array) (*array) = temp_array;
return CEED_ERROR_SUCCESS;
}
int CeedVectorGetArray(CeedVector vec, CeedMemType mem_type, CeedScalar **array) {
CeedCheck(vec->GetArray, vec->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetArray");
CeedCheck(vec->state % 2 == 0, vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, the access lock is already in use");
CeedCheck(vec->num_readers == 0, vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, a process has read access");
if (vec->length > 0) {
bool has_valid_array = true;
CeedCall(CeedVectorHasValidArray(vec, &has_valid_array));
CeedCheck(has_valid_array, vec->ceed, CEED_ERROR_BACKEND,
"CeedVector has no valid data to read, must set data with CeedVectorSetValue or CeedVectorSetArray");
CeedCall(vec->GetArray(vec, mem_type, array));
} else {
*array = NULL;
}
vec->state++;
return CEED_ERROR_SUCCESS;
}
int CeedVectorGetArrayRead(CeedVector vec, CeedMemType mem_type, const CeedScalar **array) {
CeedCheck(vec->GetArrayRead, vec->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetArrayRead");
CeedCheck(vec->state % 2 == 0, vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector read-only array access, the access lock is already in use");
if (vec->length > 0) {
bool has_valid_array = true;
CeedCall(CeedVectorHasValidArray(vec, &has_valid_array));
CeedCheck(has_valid_array, vec->ceed, CEED_ERROR_BACKEND,
"CeedVector has no valid data to read, must set data with CeedVectorSetValue or CeedVectorSetArray");
CeedCall(vec->GetArrayRead(vec, mem_type, array));
} else {
*array = NULL;
}
vec->num_readers++;
return CEED_ERROR_SUCCESS;
}
int CeedVectorGetArrayWrite(CeedVector vec, CeedMemType mem_type, CeedScalar **array) {
CeedCheck(vec->GetArrayWrite, vec->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetArrayWrite");
CeedCheck(vec->state % 2 == 0, vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, the access lock is already in use");
CeedCheck(vec->num_readers == 0, vec->ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, a process has read access");
if (vec->length > 0) {
CeedCall(vec->GetArrayWrite(vec, mem_type, array));
} else {
*array = NULL;
}
vec->state++;
return CEED_ERROR_SUCCESS;
}
int CeedVectorRestoreArray(CeedVector vec, CeedScalar **array) {
CeedCheck(vec->state % 2 == 1, vec->ceed, CEED_ERROR_ACCESS, "Cannot restore CeedVector array access, access was not granted");
if (vec->length > 0 && vec->RestoreArray) CeedCall(vec->RestoreArray(vec));
*array = NULL;
vec->state++;
return CEED_ERROR_SUCCESS;
}
int CeedVectorRestoreArrayRead(CeedVector vec, const CeedScalar **array) {
CeedCheck(vec->num_readers > 0, vec->ceed, CEED_ERROR_ACCESS, "Cannot restore CeedVector array read access, access was not granted");
vec->num_readers--;
if (vec->length > 0 && vec->num_readers == 0 && vec->RestoreArrayRead) CeedCall(vec->RestoreArrayRead(vec));
*array = NULL;
return CEED_ERROR_SUCCESS;
}
int CeedVectorNorm(CeedVector vec, CeedNormType norm_type, CeedScalar *norm) {
bool has_valid_array = true;
CeedCall(CeedVectorHasValidArray(vec, &has_valid_array));
CeedCheck(has_valid_array, vec->ceed, CEED_ERROR_BACKEND,
"CeedVector has no valid data to compute norm, must set data with CeedVectorSetValue or CeedVectorSetArray");
if (vec->length == 0) {
*norm = 0;
return CEED_ERROR_SUCCESS;
}
if (vec->Norm) {
CeedCall(vec->Norm(vec, norm_type, norm));
return CEED_ERROR_SUCCESS;
}
const CeedScalar *array;
CeedCall(CeedVectorGetArrayRead(vec, CEED_MEM_HOST, &array));
assert(array);
*norm = 0.;
switch (norm_type) {
case CEED_NORM_1:
for (CeedSize i = 0; i < vec->length; i++) {
*norm += fabs(array[i]);
}
break;
case CEED_NORM_2:
for (CeedSize i = 0; i < vec->length; i++) {
*norm += fabs(array[i]) * fabs(array[i]);
}
break;
case CEED_NORM_MAX:
for (CeedSize i = 0; i < vec->length; i++) {
const CeedScalar abs_v_i = fabs(array[i]);
*norm = *norm > abs_v_i ? *norm : abs_v_i;
}
}
if (norm_type == CEED_NORM_2) *norm = sqrt(*norm);
CeedCall(CeedVectorRestoreArrayRead(vec, &array));
return CEED_ERROR_SUCCESS;
}
int CeedVectorScale(CeedVector x, CeedScalar alpha) {
CeedScalar *x_array = NULL;
CeedSize n_x;
bool has_valid_array = true;
CeedCall(CeedVectorHasValidArray(x, &has_valid_array));
CeedCheck(has_valid_array, x->ceed, CEED_ERROR_BACKEND,
"CeedVector has no valid data to scale, must set data with CeedVectorSetValue or CeedVectorSetArray");
CeedCall(CeedVectorGetLength(x, &n_x));
if (n_x == 0) return CEED_ERROR_SUCCESS;
if (x->Scale) return x->Scale(x, alpha);
CeedCall(CeedVectorGetArray(x, CEED_MEM_HOST, &x_array));
assert(x_array);
for (CeedSize i = 0; i < n_x; i++) x_array[i] *= alpha;
CeedCall(CeedVectorRestoreArray(x, &x_array));
return CEED_ERROR_SUCCESS;
}
int CeedVectorAXPY(CeedVector y, CeedScalar alpha, CeedVector x) {
Ceed ceed_parent_x, ceed_parent_y;
CeedScalar *y_array = NULL;
CeedScalar const *x_array = NULL;
CeedSize n_x, n_y;
bool has_valid_array_x = true, has_valid_array_y = true;
CeedCall(CeedVectorGetLength(y, &n_y));
CeedCall(CeedVectorGetLength(x, &n_x));
CeedCheck(n_x == n_y, y->ceed, CEED_ERROR_UNSUPPORTED, "Cannot add vector of different lengths");
CeedCheck(x != y, y->ceed, CEED_ERROR_UNSUPPORTED, "Cannot use same vector for x and y in CeedVectorAXPY");
CeedCall(CeedVectorHasValidArray(x, &has_valid_array_x));
CeedCheck(has_valid_array_x, x->ceed, CEED_ERROR_BACKEND,
"CeedVector x has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray");
CeedCall(CeedVectorHasValidArray(y, &has_valid_array_y));
CeedCheck(has_valid_array_y, y->ceed, CEED_ERROR_BACKEND,
"CeedVector y has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray");
CeedCall(CeedGetParent(x->ceed, &ceed_parent_x));
CeedCall(CeedGetParent(y->ceed, &ceed_parent_y));
CeedCheck(ceed_parent_x == ceed_parent_y, y->ceed, CEED_ERROR_INCOMPATIBLE, "Vectors x and y must be created by the same Ceed context");
if (n_y == 0) return CEED_ERROR_SUCCESS;
if (y->AXPY) {
CeedCall(y->AXPY(y, alpha, x));
return CEED_ERROR_SUCCESS;
}
CeedCall(CeedVectorGetArray(y, CEED_MEM_HOST, &y_array));
CeedCall(CeedVectorGetArrayRead(x, CEED_MEM_HOST, &x_array));
assert(x_array);
assert(y_array);
for (CeedSize i = 0; i < n_y; i++) y_array[i] += alpha * x_array[i];
CeedCall(CeedVectorRestoreArray(y, &y_array));
CeedCall(CeedVectorRestoreArrayRead(x, &x_array));
return CEED_ERROR_SUCCESS;
}
int CeedVectorAXPBY(CeedVector y, CeedScalar alpha, CeedScalar beta, CeedVector x) {
Ceed ceed_parent_x, ceed_parent_y;
CeedScalar *y_array = NULL;
CeedScalar const *x_array = NULL;
CeedSize n_x, n_y;
bool has_valid_array_x = true, has_valid_array_y = true;
CeedCall(CeedVectorGetLength(y, &n_y));
CeedCall(CeedVectorGetLength(x, &n_x));
CeedCheck(n_x == n_y, y->ceed, CEED_ERROR_UNSUPPORTED, "Cannot add vector of different lengths");
CeedCheck(x != y, y->ceed, CEED_ERROR_UNSUPPORTED, "Cannot use same vector for x and y in CeedVectorAXPBY");
CeedCall(CeedVectorHasValidArray(x, &has_valid_array_x));
CeedCheck(has_valid_array_x, x->ceed, CEED_ERROR_BACKEND,
"CeedVector x has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray");
CeedCall(CeedVectorHasValidArray(y, &has_valid_array_y));
CeedCheck(has_valid_array_y, y->ceed, CEED_ERROR_BACKEND,
"CeedVector y has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray");
CeedCall(CeedGetParent(x->ceed, &ceed_parent_x));
CeedCall(CeedGetParent(y->ceed, &ceed_parent_y));
CeedCheck(ceed_parent_x == ceed_parent_y, y->ceed, CEED_ERROR_INCOMPATIBLE, "Vectors x and y must be created by the same Ceed context");
if (n_y == 0) return CEED_ERROR_SUCCESS;
if (y->AXPBY) {
CeedCall(y->AXPBY(y, alpha, beta, x));
return CEED_ERROR_SUCCESS;
}
CeedCall(CeedVectorGetArray(y, CEED_MEM_HOST, &y_array));
CeedCall(CeedVectorGetArrayRead(x, CEED_MEM_HOST, &x_array));
assert(x_array);
assert(y_array);
for (CeedSize i = 0; i < n_y; i++) y_array[i] += alpha * x_array[i] + beta * y_array[i];
CeedCall(CeedVectorRestoreArray(y, &y_array));
CeedCall(CeedVectorRestoreArrayRead(x, &x_array));
return CEED_ERROR_SUCCESS;
}
int CeedVectorPointwiseMult(CeedVector w, CeedVector x, CeedVector y) {
Ceed ceed_parent_w, ceed_parent_x, ceed_parent_y;
CeedScalar *w_array = NULL;
CeedScalar const *x_array = NULL, *y_array = NULL;
CeedSize n_w, n_x, n_y;
bool has_valid_array_x = true, has_valid_array_y = true;
CeedCall(CeedVectorGetLength(w, &n_w));
CeedCall(CeedVectorGetLength(x, &n_x));
CeedCall(CeedVectorGetLength(y, &n_y));
CeedCheck(n_w == n_x && n_w == n_y, w->ceed, CEED_ERROR_UNSUPPORTED, "Cannot multiply vectors of different lengths");
CeedCall(CeedGetParent(w->ceed, &ceed_parent_w));
CeedCall(CeedGetParent(x->ceed, &ceed_parent_x));
CeedCall(CeedGetParent(y->ceed, &ceed_parent_y));
CeedCheck(ceed_parent_w == ceed_parent_x && ceed_parent_w == ceed_parent_y, w->ceed, CEED_ERROR_INCOMPATIBLE,
"Vectors w, x, and y must be created by the same Ceed context");
CeedCall(CeedVectorHasValidArray(x, &has_valid_array_x));
CeedCheck(has_valid_array_x, x->ceed, CEED_ERROR_BACKEND,
"CeedVector x has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray");
CeedCall(CeedVectorHasValidArray(y, &has_valid_array_y));
CeedCheck(has_valid_array_y, y->ceed, CEED_ERROR_BACKEND,
"CeedVector y has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray");
if (n_w == 0) return CEED_ERROR_SUCCESS;
if (w->PointwiseMult) {
CeedCall(w->PointwiseMult(w, x, y));
return CEED_ERROR_SUCCESS;
}
if (x == w || y == w) {
CeedCall(CeedVectorGetArray(w, CEED_MEM_HOST, &w_array));
} else {
CeedCall(CeedVectorGetArrayWrite(w, CEED_MEM_HOST, &w_array));
}
if (x != w) {
CeedCall(CeedVectorGetArrayRead(x, CEED_MEM_HOST, &x_array));
} else {
x_array = w_array;
}
if (y != w && y != x) {
CeedCall(CeedVectorGetArrayRead(y, CEED_MEM_HOST, &y_array));
} else if (y == x) {
y_array = x_array;
} else if (y == w) {
y_array = w_array;
}
assert(w_array);
assert(x_array);
assert(y_array);
for (CeedSize i = 0; i < n_w; i++) w_array[i] = x_array[i] * y_array[i];
if (y != w && y != x) CeedCall(CeedVectorRestoreArrayRead(y, &y_array));
if (x != w) CeedCall(CeedVectorRestoreArrayRead(x, &x_array));
CeedCall(CeedVectorRestoreArray(w, &w_array));
return CEED_ERROR_SUCCESS;
}
int CeedVectorReciprocal(CeedVector vec) {
CeedSize len;
CeedScalar *array;
bool has_valid_array = true;
CeedCall(CeedVectorHasValidArray(vec, &has_valid_array));
CeedCheck(has_valid_array, vec->ceed, CEED_ERROR_BACKEND,
"CeedVector has no valid data to compute reciprocal, must set data with CeedVectorSetValue or CeedVectorSetArray");
CeedCheck(vec->state > 0, vec->ceed, CEED_ERROR_INCOMPLETE, "CeedVector must have data set to take reciprocal");
if (vec->length == 0) return CEED_ERROR_SUCCESS;
if (vec->Reciprocal) {
CeedCall(vec->Reciprocal(vec));
return CEED_ERROR_SUCCESS;
}
CeedCall(CeedVectorGetLength(vec, &len));
CeedCall(CeedVectorGetArray(vec, CEED_MEM_HOST, &array));
for (CeedSize i = 0; i < len; i++) {
if (fabs(array[i]) > CEED_EPSILON) array[i] = 1. / array[i];
}
CeedCall(CeedVectorRestoreArray(vec, &array));
return CEED_ERROR_SUCCESS;
}
int CeedVectorViewRange(CeedVector vec, CeedSize start, CeedSize stop, CeedInt step, const char *fp_fmt, FILE *stream) {
const CeedScalar *x;
char fmt[1024];
CeedCheck(step != 0, vec->ceed, CEED_ERROR_MINOR, "View range 'step' must be nonzero");
fprintf(stream, "CeedVector length %ld\n", (long)vec->length);
if (start != 0 || stop != vec->length || step != 1) {
fprintf(stream, " start: %ld\n stop: %ld\n step: %" CeedInt_FMT "\n", (long)start, (long)stop, step);
}
if (start > vec->length) start = vec->length;
if (stop > vec->length) stop = vec->length;
snprintf(fmt, sizeof fmt, " %s\n", fp_fmt ? fp_fmt : "%g");
CeedCall(CeedVectorGetArrayRead(vec, CEED_MEM_HOST, &x));
for (CeedSize i = start; step > 0 ? (i < stop) : (i > stop); i += step) fprintf(stream, fmt, x[i]);
CeedCall(CeedVectorRestoreArrayRead(vec, &x));
if (stop != vec->length) fprintf(stream, " ...\n");
return CEED_ERROR_SUCCESS;
}
int CeedVectorView(CeedVector vec, const char *fp_fmt, FILE *stream) {
CeedCall(CeedVectorViewRange(vec, 0, vec->length, 1, fp_fmt, stream));
return CEED_ERROR_SUCCESS;
}
int CeedVectorGetCeed(CeedVector vec, Ceed *ceed) {
*ceed = vec->ceed;
return CEED_ERROR_SUCCESS;
}
int CeedVectorGetLength(CeedVector vec, CeedSize *length) {
*length = vec->length;
return CEED_ERROR_SUCCESS;
}
int CeedVectorDestroy(CeedVector *vec) {
if (!*vec || *vec == CEED_VECTOR_ACTIVE || *vec == CEED_VECTOR_NONE || --(*vec)->ref_count > 0) {
*vec = NULL;
return CEED_ERROR_SUCCESS;
}
CeedCheck((*vec)->state % 2 == 0, (*vec)->ceed, CEED_ERROR_ACCESS, "Cannot destroy CeedVector, the writable access lock is in use");
CeedCheck((*vec)->num_readers == 0, (*vec)->ceed, CEED_ERROR_ACCESS, "Cannot destroy CeedVector, a process has read access");
if ((*vec)->Destroy) CeedCall((*vec)->Destroy(*vec));
CeedCall(CeedDestroy(&(*vec)->ceed));
CeedCall(CeedFree(vec));
return CEED_ERROR_SUCCESS;
}