コード例 #1
0
  /**
   * The update rule for the basis matrix W.
   * The function takes in all the matrices and only changes the
   * value of the W matrix.
   *
   * @param V Input matrix to be factorized.
   * @param W Basis matrix to be updated.
   * @param H Encoding matrix.
   */
  inline void WUpdate(const arma::sp_mat& V,
                      arma::mat& W,
                      const arma::mat& H)
  {
    if(!isStart) (*it)++;
    else isStart = false;

    if(*it == V.end())
    {
        delete it;
        it = new arma::sp_mat::const_iterator(V.begin());
    }

    size_t currentUserIndex = it->col();
    size_t currentItemIndex = it->row();

    arma::mat deltaW(1, W.n_cols);
    deltaW.zeros();

    deltaW += (**it - arma::dot(W.row(currentItemIndex), H.col(currentUserIndex)))
                                      * arma::trans(H.col(currentUserIndex));
    if(kw != 0) deltaW -= kw * W.row(currentItemIndex);

    W.row(currentItemIndex) += u*deltaW;
  }
コード例 #2
0
ファイル: sparse.cpp プロジェクト: RcppCore/RcppArmadillo
// [[Rcpp::export]]
arma::sp_mat sparseIterators(arma::sp_mat SM, double val) {
    arma::sp_mat::iterator begin = SM.begin();
    arma::sp_mat::iterator end   = SM.end();
    
    for (arma::sp_mat::iterator it = begin; it != end; ++it)
      (*it) += val;
    
    return SM;
}
コード例 #3
0
ファイル: primal_dual_impl.hpp プロジェクト: YaweiZhao/mlpack
/**
 * Solve the following KKT system (2.10) of [AHO98]:
 *
 *     [ 0  A^T  I ] [ dsx ] = [ rd ]
 *     [ A   0   0 ] [  dy ] = [ rp ]
 *     [ E   0   F ] [ dsz ] = [ rc ]
 *     \---- M ----/
 *
 * where
 *
 *     A  = [ Asparse ]
 *          [ Adense  ]
 *     dy = [ dysparse  dydense ]
 *     E  = Z sym I
 *     F  = X sym I
 *
 */
static inline void
SolveKKTSystem(const arma::sp_mat& Asparse,
               const arma::mat& Adense,
               const arma::mat& Z,
               const arma::mat& M,
               const arma::mat& F,
               const arma::vec& rp,
               const arma::vec& rd,
               const arma::vec& rc,
               arma::vec& dsx,
               arma::vec& dysparse,
               arma::vec& dydense,
               arma::vec& dsz)
{
  arma::mat Frd_rc_Mat, Einv_Frd_rc_Mat,
            Einv_Frd_ATdy_rc_Mat, Frd_ATdy_rc_Mat;
  arma::vec Einv_Frd_rc, Einv_Frd_ATdy_rc, dy;

  // Note: Whenever a formula calls for E^(-1) v for some v, we solve Lyapunov
  // equations instead of forming an explicit inverse.

  // Compute the RHS of (2.12)
  math::Smat(F * rd - rc, Frd_rc_Mat);
  SolveLyapunov(Einv_Frd_rc_Mat, Z, 2. * Frd_rc_Mat);
  math::Svec(Einv_Frd_rc_Mat, Einv_Frd_rc);

  arma::vec rhs = rp;
  const size_t numConstraints = Asparse.n_rows + Adense.n_rows;
  if (Asparse.n_rows)
    rhs(arma::span(0, Asparse.n_rows - 1)) += Asparse * Einv_Frd_rc;
  if (Adense.n_rows)
    rhs(arma::span(Asparse.n_rows, numConstraints - 1)) += Adense * Einv_Frd_rc;

  // TODO(stephentu): use a more efficient method (e.g. LU decomposition)
  if (!arma::solve(dy, M, rhs))
    Log::Fatal << "PrimalDualSolver::SolveKKTSystem(): Could not solve KKT "
        << "system." << std::endl;

  if (Asparse.n_rows)
    dysparse = dy(arma::span(0, Asparse.n_rows - 1));
  if (Adense.n_rows)
    dydense = dy(arma::span(Asparse.n_rows, numConstraints - 1));

  // Compute dx from (2.13)
  math::Smat(F * (rd - Asparse.t() * dysparse - Adense.t() * dydense) - rc,
      Frd_ATdy_rc_Mat);
  SolveLyapunov(Einv_Frd_ATdy_rc_Mat, Z, 2. * Frd_ATdy_rc_Mat);
  math::Svec(Einv_Frd_ATdy_rc_Mat, Einv_Frd_ATdy_rc);
  dsx = -Einv_Frd_ATdy_rc;

  // Compute dz from (2.14)
  dsz = rd - Asparse.t() * dysparse - Adense.t() * dydense;
}
コード例 #4
0
void CalculateEdgeLengths(vtkPolyData* mesh, arma::sp_mat& edge_lengths) {
    vtkSmartPointer<vtkIdList> cellIdList = vtkSmartPointer<vtkIdList>::New();
    vtkSmartPointer<vtkIdList> pointIdList = vtkSmartPointer<vtkIdList>::New();

    const int npts = mesh->GetNumberOfPoints();
    edge_lengths.set_size(npts, npts);

    for (vtkIdType i = 0; i < npts; i++) {
        cellIdList->Reset();
        mesh->GetPointCells(i, cellIdList);
        double *pt1 = mesh->GetPoint(i);


        for (vtkIdType j = 0; j < cellIdList->GetNumberOfIds(); j++) {
            pointIdList->Reset();
            mesh->GetCellPoints(cellIdList->GetId(i), pointIdList);
            for (vtkIdType k = 0; k < pointIdList->GetNumberOfIds(); k++) {
                vtkIdType pt2_id = pointIdList->GetId(k);
                if (pt2_id != i) { //not the same point
                    if (edge_lengths(i, pt2_id) != 0) { //is not calculated yet
                        double *pt2 = mesh->GetPoint(pt2_id);

                        const double dx = pt1[0] - pt2[0];
                        const double dy = pt1[1] - pt2[1];
                        const double dz = pt1[2] - pt2[2];
                        const double d = sqrt(dx * dx + dy * dy + dz * dz);

                        edge_lengths(i, pt2_id) = d;
                        edge_lengths(pt2_id, i) = d;
                    }
                }
            }
        }
    }
}
コード例 #5
0
  void Initialize(const arma::sp_mat& dataset, const size_t rank)
  {
    (void)rank;
    n = dataset.n_rows;
    m = dataset.n_cols;

    it = new arma::sp_mat::const_iterator(dataset.begin());
    isStart = true;
  }
コード例 #6
0
inline void SVDIncompleteIncrementalLearning::
                                    WUpdate<arma::sp_mat>(const arma::sp_mat& V,
                                                          arma::mat& W,
                                                          const arma::mat& H)
{
  arma::mat deltaW(n, W.n_cols);
  deltaW.zeros();
  for(arma::sp_mat::const_iterator it = V.begin_col(currentUserIndex);
                                      it != V.end_col(currentUserIndex);it++)
  {
    double val = *it;
    size_t i = it.row();
    deltaW.row(i) += (val - arma::dot(W.row(i), H.col(currentUserIndex))) *
                                         arma::trans(H.col(currentUserIndex));
    if(kw != 0) deltaW.row(i) -= kw * W.row(i);
  }

  W += u*deltaW;
}
コード例 #7
0
ファイル: lrsdp_test.cpp プロジェクト: AmesianX/mlpack
/**
 * Create an unweighted graph laplacian from the edges.
 */
void CreateSparseGraphLaplacian(const arma::mat& edges,
                                arma::sp_mat& laplacian)
{
  // Get the number of vertices in the problem.
  const size_t vertices = max(max(edges)) + 1;

  laplacian.zeros(vertices, vertices);

  for (size_t i = 0; i < edges.n_cols; ++i)
  {
    laplacian(edges(0, i), edges(1, i)) = -1.0;
    laplacian(edges(1, i), edges(0, i)) = -1.0;
  }

  for (size_t i = 0; i < vertices; ++i)
  {
    laplacian(i, i) = -arma::accu(laplacian.row(i));
  }
}
コード例 #8
0
ファイル: test_functions.cpp プロジェクト: sbrodehl/mlpack
void GeneralizedRosenbrockFunction::Gradient(const arma::mat& coordinates,
                                             const size_t i,
                                             arma::sp_mat& gradient) const
{
  gradient.set_size(n);

  const size_t p = visitationOrder[i];

  gradient[p] = 400 * (std::pow(coordinates[p], 3) - coordinates[p] *
      coordinates[p + 1]) + 2 * (coordinates[p] - 1);
  gradient[p + 1] = 200 * (coordinates[p + 1] - std::pow(coordinates[p], 2));
}
コード例 #9
0
inline void SVDIncompleteIncrementalLearning::
                              HUpdate<arma::sp_mat>(const arma::sp_mat& V,
                                                    const arma::mat& W,
                                                    arma::mat& H)
{
  arma::mat deltaH(H.n_rows, 1);
  deltaH.zeros();

  for(arma::sp_mat::const_iterator it = V.begin_col(currentUserIndex);
                                        it != V.end_col(currentUserIndex);it++)
  {
    double val = *it;
    size_t i = it.row();
    if((val = V(i, currentUserIndex)) != 0)
      deltaH += (val - arma::dot(W.row(i), H.col(currentUserIndex))) *
                                                    arma::trans(W.row(i));
  }
  if(kh != 0) deltaH -= kh * H.col(currentUserIndex);

  H.col(currentUserIndex++) += u * deltaH;
  currentUserIndex = currentUserIndex % m;
}
コード例 #10
0
void CalculateInversiveDistanceE2(const arma::sp_mat& l, const arma::vec& gamma, arma::sp_mat& I) {
    const int npts = gamma.n_elem;
    I.set_size(npts, npts);

    //the measure is symmetric, process only upper triangle
    for (int i = 0; i < npts; i++) {
        for (int j = i + 1; j < npts; j++) {
            if (l(i, j) > 0) { //only for existing edges
                const double I_ij = (l(i, j) * l(i, j) - gamma(i) * gamma(i) - gamma(j) * gamma(j)) / (2 * gamma(i) * gamma(j));
                I(i, j) = I_ij;
                I(j, i) = I_ij;
            }
        }
    }

}
コード例 #11
0
void CalculateGeneralizedEdgeLengthsE2(const arma::vec& gamma, const arma::sp_mat& I, arma::sp_mat& gen_edge_lengths) {
    const int npts = gamma.n_elem;
    gen_edge_lengths.set_size(npts, npts);

    //the measure is symmetric, process only upper triangle
    for (int i = 0; i < npts; i++) {
        for (int j = i + 1; j < npts; j++) {
            if (I(i, j) > 0) { //only for existing edges
                const double l_ij = sqrt(gamma(i) * gamma(i) + gamma(j) * gamma(j) + 2 * gamma(i) * gamma(j) * I(i, j));
                gen_edge_lengths(i, j) = l_ij;
                gen_edge_lengths(j, i) = l_ij;
            }
        }
    }

}
コード例 #12
0
void LogisticRegressionFunction<MatType>::PartialGradient(
    const arma::mat& parameters,
    const size_t j,
    arma::sp_mat& gradient) const
{
  const arma::rowvec diffs = responses - (1 / (1 + arma::exp(-parameters(0, 0)
      - parameters.tail_cols(parameters.n_elem - 1) * predictors)));

  gradient.set_size(arma::size(parameters));

  if (j == 0)
  {
    gradient[j] = -arma::accu(diffs);
  }
  else
  {
    gradient[j] = arma::dot(-predictors.row(j - 1), diffs) + lambda *
      parameters(0, j);
  }
}
コード例 #13
0
ファイル: plot.cpp プロジェクト: cran/netdiffuseR
//' Compute ego/alter edge coordinates considering alter's size and aspect ratio
//'
//' Given a graph, vertices' positions and sizes, calculates the absolute positions
//' of the endpoints of the edges considering the plot's aspect ratio.
//'
//' @param graph A square matrix of size \eqn{n}. Adjacency matrix.
//' @param toa Integer vector of size \eqn{n}. Times of adoption.
//' @param x Numeric vector of size \eqn{n}. x-coordinta of vertices.
//' @param y Numeric vector of size \eqn{n}. y-coordinta of vertices.
//' @param vertex_cex Numeric vector of size \eqn{n}. Vertices' sizes in terms
//' of the x-axis (see \code{\link{symbols}}).
//' @param undirected Logical scalar. Whether the graph is undirected or not.
//' @param no_contemporary Logical scalar. Whether to return (compute) edges'
//' coordiantes for vertices with the same time of adoption (see details).
//' @param dev Numeric vector of size 2. Height and width of the device (see details).
//' @param ran Numeric vector of size 2. Range of the x and y axis (see details).
//' @param curved Logical vector.
//' @return A numeric matrix of size \eqn{m\times 5}{m * 5} with the following
//' columns:
//' \item{x0, y0}{Edge origin}
//' \item{x1, y1}{Edge target}
//' \item{alpha}{Relative angle between \code{(x0,y0)} and \code{(x1,y1)} in terms
//' of radians}
//' With \eqn{m} as the number of resulting edges.
//' @details
//'
//' In order to make the plot's visualization more appealing, this function provides
//' a straight forward way of computing the tips of the edges considering the
//' aspect ratio of the axes range. In particular, the following corrections are
//' made at the moment of calculating the egdes coords:
//'
//' \itemize{
//' \item{Instead of using the actual distance between ego and alter, a relative
//' one is calculated as follows
//' \deqn{d'=\left[(x_0-x_1)^2 + (y_0' - y_1')^2\right]^\frac{1}{2}}{d'=sqrt[(x0-x1)^2 + (y0'-y1')^2]}
//' where \eqn{%
//' y_i'=y_i\times\frac{\max x - \min x}{\max y - \min y} }{%
//' yi' = yi * [max(x) - min(x)]/[max(y) - min(y)]}
//' }
//' \item{Then, for the relative elevation angle, \code{alpha}, the relative distance \eqn{d'}
//' is used, \eqn{\alpha'=\arccos\left( (x_0 - x_1)/d' \right)}{\alpha' = acos[ (x0 - x1)/d' ]}}
//' \item{Finally, the edge's endpoint's (alter) coordinates are computed as follows: %
//' \deqn{%
//'   x_1' = x_1 + \cos(\alpha')\times v_1}{%
//'   x1' = x1 + cos(\alpha') * v1
//' }
//' \deqn{%
//'   y_1' = y_1 -+ \sin(\alpha')\times v_1 \times\frac{\max y - \min y}{\max x - \min x} }{%
//'   y1' = y1 -+ sin(\alpha')*[max(y) - min(y)]/[max(x) - min(x)]
//' }
//' Where \eqn{v_1}{v1} is alter's size in terms of the x-axis, and the sign of
//' the second term in \eqn{y_1'}{y1'} is negative iff \eqn{y_0 < y_1}{y0<y1}.
//' }
//' }
//'
//' The same process (with sign inverted) is applied to the edge starting piont.
//' The resulting values, \eqn{x_1',y_1'}{x1',y1'} can be used with the function
//' \code{\link{arrows}}. This is the workhorse function used in \code{\link{plot_threshold}}.
//'
//' The \code{dev} argument provides a reference to rescale the plot accordingly
//' to the device, and former, considering the size of the margins as well (this
//' can be easily fetched via \code{par("pin")}, plot area in inches).
//'
//' On the other hand, \code{ran} provides a reference for the adjustment
//' according to the range of the data, this is \code{range(x)[2] - range(x)[1]}
//' and \code{range(y)[2] - range(y)[1]} respectively.
//'
//' @keywords misc dplot
//' @examples
//' # --------------------------------------------------------------------------
//' data(medInnovationsDiffNet)
//' library(sna)
//'
//' # Computing coordinates
//' set.seed(79)
//' coords <- sna::gplot(as.matrix(medInnovationsDiffNet$graph[[1]]))
//'
//' # Getting edge coordinates
//' vcex <- rep(1.5, nnodes(medInnovationsDiffNet))
//' ecoords <- edges_coords(
//'   medInnovationsDiffNet$graph[[1]],
//'   diffnet.toa(medInnovationsDiffNet),
//'   x = coords[,1], y = coords[,2],
//'   vertex_cex = vcex,
//'   dev = par("pin")
//'   )
//'
//' ecoords <- as.data.frame(ecoords)
//'
//' # Plotting
//' symbols(coords[,1], coords[,2], circles=vcex,
//'   inches=FALSE, xaxs="i", yaxs="i")
//'
//' with(ecoords, arrows(x0,y0,x1,y1, length=.1))
//' @export
// [[Rcpp::export]]
NumericMatrix edges_coords(
    const arma::sp_mat & graph,
    const arma::colvec & toa,
    const arma::colvec & x,
    const arma::colvec & y,
    const arma::colvec & vertex_cex,
    bool undirected=true,
    bool no_contemporary=true,
    NumericVector dev = NumericVector::create(),
    NumericVector ran = NumericVector::create(),
    LogicalVector curved = LogicalVector::create()
) {

  // The output matrix has the following
  // - x0 and y0
  // - x1 and y1
  // - alpha
  std::vector< double > x0;
  std::vector< double > y0;
  std::vector< double > x1;
  std::vector< double > y1;
  std::vector< double > alpha;

  // Rescaling the vertex sizes
  arma::colvec vertex_size(vertex_cex);

  // If yexpand is too small, just throw an error
  if (ran.length() == 0) {
    ran = NumericVector::create(2);
    ran[0] = x.max() - x.min();
    ran[1] = y.max() - y.min();
  }

  // Expansion factor for y
  double yexpand = 1.0;
  if ( ran[1] > 1e-5 ) yexpand = ran[1]/ran[0];

  // Adjusting for device size
  if (dev.length() == 0)
    dev = NumericVector::create(2,1.0);

  // Curved?
  if (curved.length() == 0)
    curved = LogicalVector::create(graph.n_nonzero, true);

  yexpand = yexpand * (dev[0]/dev[1]);

  for(arma::sp_mat::const_iterator it = graph.begin(); it != graph.end(); ++it) {

    int i = it.row();
    int j = it.col();

    // Checking conditions
    if (undirected && (i < j)) continue;
    if (no_contemporary && (toa(i)==toa(j)) ) continue;

    // Computing angle
    double a = atan2((y(j) - y(i))/yexpand, x(j) - x(i));
    alpha.push_back(a);

    // Adding the xs and the ys
    x0.push_back(x.at(i) + cos(a)*vertex_size.at(i));
    x1.push_back(x.at(j) - cos(a)*vertex_size.at(j));

    // The formula needs an extra help to figure out the ys
    y0.push_back(y.at(i) + sin(a)*vertex_size.at(i)*yexpand);
    y1.push_back(y.at(j) - sin(a)*vertex_size.at(j)*yexpand);
  }

  // Building up the output
  int e = x0.size();
  NumericMatrix out(e,5);
  for(int i=0; i<e; ++i) {
    out(i,0) = x0[i];
    out(i,1) = y0[i];
    out(i,2) = x1[i];
    out(i,3) = y1[i];
    out(i,4) = alpha[i];
  }

  colnames(out) = CharacterVector::create("x0", "y0", "x1", "y1", "alpha");

  return out;

}
コード例 #14
0
ファイル: sparse.cpp プロジェクト: RcppCore/RcppArmadillo
// [[Rcpp::export]]
arma::sp_mat sparseTranspose(arma::sp_mat SM) {
    return SM.t();
}
コード例 #15
0
// compute the log likelihood and its gradient w.r.t. theta
int dtq::compGrad(void)
{
  // remember, everything here is for equispaced data
  // we'll save the non-equispaced case for our scala + spark code :)
  if ((! haveData) || (! haveMyh)) return 1;
  if (spi<1) return 1;

  loglikmat = arma::zeros(ltvec-1,numts);

  if (spi==1) // special case
  {
  } 
  else
  {
    // strategy: precompute and store common elements in Mats and Cubs

    // compute gradf and gradg at all spatial grid points
    arma::mat gradfy = arma::zeros(ylen,curtheta.n_elem);
    arma::mat gradgy = arma::zeros(ylen,curtheta.n_elem);
    this->gradFGyvec(gradfy, gradgy);

    // ompute gradf and gradg at all the data points
    arma::cube gradfdata = arma::zeros(curtheta.n_elem, (ltvec-1), numts);
    arma::cube gradgdata = arma::zeros(curtheta.n_elem, (ltvec-1), numts);
    this->gradFGdata(gradfdata, gradgdata);
    
    // initialize cubes to store all states and adjoints,
    // at all internal time points (spi-1),
    // for each pair of time series points (ltvec-1),
    // and at all spatial grid points (ylen)
    arma::cube dtqcube = arma::zeros(ylen,(ltvec-1),(spi-1));
    arma::cube adjcube = arma::zeros(ylen,(ltvec-1),(spi-1));

    // temporary matrix to store the initial state, phatinit
    arma::mat phatinit = arma::zeros(ylen,(ltvec-1));
    
    // cube to store the gradient of the initial state w.r.t. theta
    arma::cube phatgrad = arma::zeros(ylen,(ltvec-1),curtheta.n_elem);

    // build the big matrix of initial conditions
    // and the gradients of those initial conditions!
    this->phatinitgrad(phatinit, phatgrad, gradfdata, gradgdata);
    dtqcube.slice(0) = phatinit;

    // propagate states forward in time by (spi-2) steps
    if (spi >= 3)
      for (int i=1; i<=(spi-2); i++)
        dtqcube.slice(i) = myk * prop * dtqcube.slice(i-1);

    // now multiply on the left by the Gamma vectors
    const arma::vec muvec = yvec + fy*myh;
    const arma::vec sigvec = gy*sqrt(myh);
    arma::cube allgamma = arma::zeros(ylen,numts,(ltvec-1));
    for (int j=0; j<(ltvec-1); j++)
    {
      for (int l=0; l<numts; l++)
      {
        allgamma.slice(j).col(l) = myk*gausspdf((*odata)(j+1,l),muvec,sigvec);
        loglikmat(j,l) = log(arma::dot(allgamma.slice(j).col(l),dtqcube.slice(spi-2).col(j)));
      }
    }

    // std::cout << loglikmat << '\n';

    // initialize the adjoint calculation
    for (int j=0; j<(ltvec-1); j++)
      for (int l=0; l<numts; l++)
        adjcube.slice(spi-2).col(j) += allgamma.slice(j).col(l) / exp(loglikmat(j,l));

    // propagate adjoints backward in time by (spi-2) steps
    arma::sp_mat transprop = prop.t();
    if (spi >= 3)
      for (int i=(spi-2); i>=1; i--)
        adjcube.slice(i-1) = myk * transprop * adjcube.slice(i);

    // stuff that we need for a bunch of gradients
    gradloglik = arma::zeros(curtheta.n_elem);
    arma::vec gvecm1 = arma::pow(gy,-1);
    arma::vec gvecm2 = arma::pow(gy,-2);
    arma::vec gvecm3 = arma::pow(gy,-3);

    // actual gradient calculation
    // proceed element-wise through theta_i
    for (int i=0; i<curtheta.n_elem; i++)
    {
      arma::vec temp1 = gvecm2 % gradfy.col(i);
      arma::vec temp2 = gvecm1 % gradgy.col(i);
      arma::vec temp3 = (1.0/myh)*gvecm3 % gradgy.col(i);
      arma::sp_mat::const_iterator start = prop.begin();
      arma::sp_mat::const_iterator end = prop.end();
      arma::umat dkdtloc(2, prop.n_nonzero);
      arma::vec dkdtval(prop.n_nonzero);
      unsigned int dkdtc = 0;
      for (arma::sp_mat::const_iterator it = start; it != end; ++it)
      {
        dkdtloc(0,dkdtc) = it.row();
        dkdtloc(1,dkdtc) = it.col();
        dkdtc++;
      }
#pragma omp parallel for
      for (unsigned int dkdtcount=0; dkdtcount < prop.n_nonzero; dkdtcount++)
      {
        unsigned int orow = dkdtloc(0,dkdtcount);
        unsigned int ocol = dkdtloc(1,dkdtcount);
        double comval = yvec(orow) - muvec(ocol);
        dkdtval(dkdtcount) = myk*(prop.values[dkdtcount])*( comval*temp1(ocol) - temp2(ocol) + temp3(ocol)*comval*comval );
      }
      arma::sp_mat dkdtheta(dkdtloc, dkdtval, ylen, ylen, false, true);

      // implement formula (22) from the DSAA paper
      // need gradient of Gamma{F-1}
      double tally = 0.0;
#pragma omp parallel for reduction(+:tally)
      for (int j=0; j<(ltvec-1); j++)
      {
        tally += arma::dot(phatgrad.slice(i).col(j),adjcube.slice(0).col(j));
      }

#pragma omp parallel for collapse(2) reduction(+:tally)
      for (int j=0; j<(ltvec-1); j++)
        for (int l=0; l<numts; l++)
        {
          double xi = (*odata)((j+1),l);
          arma::vec gammagrad = (xi-muvec) % temp1;
          gammagrad += arma::pow(xi-muvec,2) % temp3;
          gammagrad -= temp2;
          gammagrad = gammagrad % allgamma.slice(j).col(l);
          tally += arma::dot(gammagrad,dtqcube.slice(spi-2).col(j)) / exp(loglikmat(j,l));
        }

      // we have tested and found that the dot product is better than the
      // triple matrix product here, i.e., it is worth taking the transpose
      // arma::mat dkdtheta = dkdthetatrans.t();
#pragma omp parallel for collapse(2) reduction(+:tally)
      for (int j=0; j<(ltvec-1); j++)
        for (int l=0; l<(spi-2); l++)
        {
          tally += arma::dot((dkdtheta*dtqcube.slice(l).col(j)),adjcube.slice(l+1).col(j));
        }
      gradloglik(i) = tally;
    }
  }
  haveLoglik = true;
  haveGradloglik = true;
  return 0;
}