static void bdm_matvec(void* context, real_t* x, real_t* Ax) { bdm_t* A = context; for (int i = 0; i < A->num_block_rows; ++i) { int bs = A->B_offsets[i+1] - A->B_offsets[i]; real_t* Ai = &A->D[A->D_offsets[i]]; real_t* xi = &x[A->B_offsets[i]]; real_t* Axi = &Ax[A->B_offsets[i]]; char no_trans = 'N'; real_t one = 1.0, zero = 0.0; int incx = 1; rgemv(&no_trans, &bs, &bs, &one, Ai, &bs, xi, &incx, &zero, Axi, &incx); } }
static void mls_compute(void* context, int i, point_t* x, real_t* values, vector_t* gradients) { mls_t* mls = context; int N = mls->N; // Compute the kernels and their gradients at x. real_t W[N]; vector_t grad_W[N]; shape_function_kernel_compute(mls->W, mls->xj, mls->hj, N, x, W, grad_W); // Compute the moment matrix A. int dim = mls->basis_dim; real_t A[dim*dim], AinvB[dim*N]; memset(A, 0, sizeof(real_t)*dim*dim); for (int n = 0; n < N; ++n) { for (int i = 0; i < dim; ++i) { AinvB[dim*n+i] = W[n] * mls->basis[dim*n+i]; for (int j = 0; j < dim; ++j) A[dim*j+i] += W[n] * mls->basis[dim*n+i] * mls->basis[dim*n+j]; } } // Factor the moment matrix. char uplo = 'L'; int info; rpotrf(&uplo, &dim, A, &dim, &info); ASSERT(info == 0); // Compute Ainv * B. rpotrs(&uplo, &dim, &N, A, &dim, AinvB, &dim, &info); ASSERT(info == 0); // values^T = basis^T * Ainv * B (or values = (Ainv * B)^T * basis.) real_t alpha = 1.0, beta = 0.0; int one = 1; char trans = 'T'; real_t basis_x[dim]; polynomial_compute_basis(mls->poly_degree, 0, 0, 0, x, basis_x); //printf("y = %g %g %g, basis = ", y.x, y.y, y.z); //for (int i = 0; i < dim; ++i) //printf("%g ", basis_x[i]); //printf("\n"); rgemv(&trans, &dim, &N, &alpha, AinvB, &dim, basis_x, &one, &beta, values, &one); // If we are in the business of computing gradients, compute the // partial derivatives of Ainv * B. if (gradients != NULL) { // Compute the derivative of A inverse. We'll need the derivatives of // A and B first. real_t dAdx[dim*dim], dAdy[dim*dim], dAdz[dim*dim], dBdx[dim*N], dBdy[dim*N], dBdz[dim*N]; memset(dAdx, 0, sizeof(real_t)*dim*dim); memset(dAdy, 0, sizeof(real_t)*dim*dim); memset(dAdz, 0, sizeof(real_t)*dim*dim); for (int n = 0; n < N; ++n) { for (int i = 0; i < dim; ++i) { real_t basis_i = mls->basis[dim*n+i]; dBdx[dim*n+i] = grad_W[n].x*basis_i; dBdy[dim*n+i] = grad_W[n].y*basis_i; dBdz[dim*n+i] = grad_W[n].z*basis_i; for (int j = 0; j < dim; ++j) { real_t basis_j = mls->basis[dim*n+j]; dAdx[dim*j+i] += grad_W[n].x * basis_i * basis_j; dAdy[dim*j+i] += grad_W[n].y * basis_i * basis_j; dAdz[dim*j+i] += grad_W[n].z * basis_i * basis_j; } } } // The partial derivatives of A inverse are: // d(Ainv) = -Ainv * dA * Ainv, so // d(Ainv * B) = -Ainv * dA * Ainv * B + Ainv * dB // = Ainv * (-dA * Ainv * B + dB). // We left-multiply Ainv*B by the gradient of A, placing the results // in dAinvBdx, dAinvBdy, and dAinvBdz. real_t alpha = 1.0, beta = 0.0; real_t dAinvBdx[dim*N], dAinvBdy[dim*N], dAinvBdz[dim*N]; char no_trans = 'N'; rgemm(&no_trans, &no_trans, &dim, &N, &dim, &alpha, dAdx, &dim, AinvB, &dim, &beta, dAinvBdx, &dim); rgemm(&no_trans, &no_trans, &dim, &N, &dim, &alpha, dAdy, &dim, AinvB, &dim, &beta, dAinvBdy, &dim); rgemm(&no_trans, &no_trans, &dim, &N, &dim, &alpha, dAdz, &dim, AinvB, &dim, &beta, dAinvBdz, &dim); // Flip the sign of dA * Ainv * B, and add dB. for (int i = 0; i < dim*N; ++i) { dAinvBdx[i] = -dAinvBdx[i] + dBdx[i]; dAinvBdy[i] = -dAinvBdy[i] + dBdy[i]; dAinvBdz[i] = -dAinvBdz[i] + dBdz[i]; } // Now "left-multiply by Ainv" by solving the equation (e.g.) // A * (dAinvBdx) = (-dA * Ainv * B + dB). rpotrs(&uplo, &dim, &N, A, &dim, dAinvBdx, &dim, &info); ASSERT(info == 0); rpotrs(&uplo, &dim, &N, A, &dim, dAinvBdy, &dim, &info); ASSERT(info == 0); rpotrs(&uplo, &dim, &N, A, &dim, dAinvBdz, &dim, &info); ASSERT(info == 0); // Now compute the gradients. // 1st term: gradient of basis, dotted with Ainv * B. real_t dpdx[dim], dpdy[dim], dpdz[dim]; polynomial_compute_basis(mls->poly_degree, 1, 0, 0, x, dpdx); polynomial_compute_basis(mls->poly_degree, 0, 1, 0, x, dpdy); polynomial_compute_basis(mls->poly_degree, 0, 0, 1, x, dpdz); real_t dpdx_AinvB[N], dpdy_AinvB[N], dpdz_AinvB[N]; rgemv(&trans, &dim, &N, &alpha, AinvB, &dim, dpdx, &one, &beta, dpdx_AinvB, &one); rgemv(&trans, &dim, &N, &alpha, AinvB, &dim, dpdy, &one, &beta, dpdy_AinvB, &one); rgemv(&trans, &dim, &N, &alpha, AinvB, &dim, dpdz, &one, &beta, dpdz_AinvB, &one); // Second term: basis_x dotted with gradient of Ainv * B. real_t p_dAinvBdx[N], p_dAinvBdy[N], p_dAinvBdz[N]; rgemv(&trans, &dim, &N, &alpha, dAinvBdx, &dim, basis_x, &one, &beta, p_dAinvBdx, &one); rgemv(&trans, &dim, &N, &alpha, dAinvBdy, &dim, basis_x, &one, &beta, p_dAinvBdy, &one); rgemv(&trans, &dim, &N, &alpha, dAinvBdz, &dim, basis_x, &one, &beta, p_dAinvBdz, &one); // Gradients are the sum of these terms. for (int i = 0; i < N; ++i) { gradients[i].x = dpdx_AinvB[i] + p_dAinvBdx[i]; gradients[i].y = dpdy_AinvB[i] + p_dAinvBdy[i]; gradients[i].z = dpdz_AinvB[i] + p_dAinvBdz[i]; } } }