public Instance convertInstance(Instance instance) throws Exception {
    if (m_s == null) {
      throw new Exception("convertInstance: Latent Semantic Analysis not " +
                           "performed yet.");
    }
    
    // array to hold new attribute values
    double [] newValues = new double[m_outputNumAttributes];
    
    // apply filters so new instance is in same format as training instances
    Instance tempInstance = (Instance)instance.copy();
    if (!instance.dataset().equalHeaders(m_trainHeader)) {
      throw new Exception("Can't convert instance: headers don't match: " +
      "LatentSemanticAnalysis\n" + instance.dataset().equalHeadersMsg(m_trainHeader));
    }
    // replace missing values
    m_replaceMissingFilter.input(tempInstance);
    m_replaceMissingFilter.batchFinished();
    tempInstance = m_replaceMissingFilter.output();
    // normalize
    if (m_normalize) {
      m_normalizeFilter.input(tempInstance);
      m_normalizeFilter.batchFinished();
      tempInstance = m_normalizeFilter.output();
    }
    // convert nominal attributes to binary
    m_nominalToBinaryFilter.input(tempInstance);
    m_nominalToBinaryFilter.batchFinished();
    tempInstance = m_nominalToBinaryFilter.output();
    // remove class/other attributes
    if (m_attributeFilter != null) {
      m_attributeFilter.input(tempInstance);
      m_attributeFilter.batchFinished();
      tempInstance = m_attributeFilter.output();
    }
    
    // record new attribute values
    if (m_hasClass) { // copy class value
      newValues[m_outputNumAttributes - 1] = instance.classValue();
    }
    double [][] oldInstanceValues = new double[1][m_numAttributes];
    oldInstanceValues[0] = tempInstance.toDoubleArray();
    Matrix instanceVector = new Matrix(oldInstanceValues); // old attribute values
    instanceVector = instanceVector.times(m_transformationMatrix); // new attribute values
    for (int i = 0; i < m_actualRank; i++) {
      newValues[i] = instanceVector.get(0, i);
    }
    
    // return newly transformed instance
    if (instance instanceof SparseInstance) {
      return new SparseInstance(instance.weight(), newValues);
    } else {
      return new DenseInstance(instance.weight(), newValues);
    }
  }