@Override
  public void encode(CalibratedPoseAndPoint model, double[] param) {
    int paramIndex = 0;

    // first decode the transformation
    for (int i = 0; i < numViews; i++) {
      // don't encode if it is already known
      if (knownView[i]) continue;

      Se3_F64 se = model.getWorldToCamera(i);

      // force the "rotation matrix" to be an exact rotation matrix
      // otherwise Rodrigues will have issues with the noise
      if (!svd.decompose(se.getR())) throw new RuntimeException("SVD failed");

      DenseMatrix64F U = svd.getU(null, false);
      DenseMatrix64F V = svd.getV(null, false);

      CommonOps.multTransB(U, V, R);

      // extract Rodrigues coordinates
      RotationMatrixGenerator.matrixToRodrigues(R, rotation);

      param[paramIndex++] = rotation.unitAxisRotation.x * rotation.theta;
      param[paramIndex++] = rotation.unitAxisRotation.y * rotation.theta;
      param[paramIndex++] = rotation.unitAxisRotation.z * rotation.theta;

      Vector3D_F64 T = se.getT();

      param[paramIndex++] = T.x;
      param[paramIndex++] = T.y;
      param[paramIndex++] = T.z;
    }

    for (int i = 0; i < numPoints; i++) {
      Point3D_F64 p = model.getPoint(i);

      param[paramIndex++] = p.x;
      param[paramIndex++] = p.y;
      param[paramIndex++] = p.z;
    }
  }
  /**
   * Computes a basis (the principle components) from the most dominant eigenvectors.
   *
   * @param numComponents Number of vectors it will use to describe the data. Typically much smaller
   *     than the number of elements in the input vector.
   */
  public void computeBasis(int numComponents) {
    if (numComponents > A.getNumCols())
      throw new IllegalArgumentException("More components requested that the data's length.");
    if (sampleIndex != A.getNumRows())
      throw new IllegalArgumentException("Not all the data has been added");
    if (numComponents > sampleIndex)
      throw new IllegalArgumentException(
          "More data needed to compute the desired number of components");

    this.numComponents = numComponents;

    // compute the mean of all the samples
    for (int i = 0; i < A.getNumRows(); i++) {
      for (int j = 0; j < mean.length; j++) {
        mean[j] += A.get(i, j);
      }
    }
    for (int j = 0; j < mean.length; j++) {
      mean[j] /= A.getNumRows();
    }

    // subtract the mean from the original data
    for (int i = 0; i < A.getNumRows(); i++) {
      for (int j = 0; j < mean.length; j++) {
        A.set(i, j, A.get(i, j) - mean[j]);
      }
    }

    // Compute SVD and save time by not computing U
    SingularValueDecomposition<DenseMatrix64F> svd =
        DecompositionFactory.svd(A.numRows, A.numCols, false, true, false);
    if (!svd.decompose(A)) throw new RuntimeException("SVD failed");

    V_t = svd.getV(null, true);
    DenseMatrix64F W = svd.getW(null);

    // Singular values are in an arbitrary order initially
    SingularOps.descendingOrder(null, false, W, V_t, true);

    // strip off unneeded components and find the basis
    V_t.reshape(numComponents, mean.length, true);
  }