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);
  }
}
Example #2
0
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];
    }
  }
}