R/MxPPML.R

Defines functions PPML.Tool.fakeLatentFoldout PPML.Tool.PathToMatrix PPML.Tool.EnumeratePermutations PPML.Test.Battery.LowLevel PPML.Tool.EnumerateMissingnessPatterns imxPPML.Test.Battery PPML.Test.CheckFits imxPPML.Test.Test PPML.Tool.CheckPPMLDidEqualOrBetter selectSubModelFData PPML.Split PPML.SolveOrPartialSolve PPML.Transform PPMLMissingData PPML.Post.UnfoldNHEV buildCErr PPML.Pre.FixNHEV PPML.Pre.FakeLatents PPMLClassifyLatents PPML.Pre.UpdateSolveType PPML.Check.UseOptimizer PPML.CheckApplicable PPMLTransformModel single.na imxPPML

	# Cases: Solvable, and Optimizable

	# Solvable Case
	# I -- Name anonymous parameters in model
	# II -- Apply pretransforms to model
	# III -- Apply transform to model
	# IV --- Solve using transformed model
	# V --- Plug parameters back in to named model
	# VI --- Plug values matrix from named model back in to original model
	# return original model

	# Optimizable case - Partial Solution
	# I -- Name anonymous parameters in model
	# II -- Apply pretransforms to model
	# III --- Apply transform to model
	# IV -- Split model, solve for variance, plug back in to leftmodel
	# V -- Call optimizer on model
	# VI -- Extract parameters and reinsert to named model
	# VII -- Plug values matrix from named model back in to original model
	# return original model

	# Optimizable case - Missing Data
	# I -- Name anonymous parameters in model
	# II -- Apply pretransforms to model
	# III -- Split model for missingness patterns
	# IV -- Apply transform to each applicable submodel
	# V -- Pass overall model to optimizer
	# VI -- Extract parameters and reinsert to named model
	# VII -- Plug values matrix from named model back in to original model

	# Optimizable case - Split Model
	# I -- Name anonymous parameters in model
	# II -- Apply pretransforms to model
	# III -- Apply transform to model
	# IV -- Split model
	# V -- Call optimizer on model
	# VI -- Extract parameters and reinsert to named model
	# VII - Plug values matrix from named model back in to original model

	# SO:
	# I -- Name anonymous parameters in model
	# II -- Apply pretransforms to model
	# III --  Missing data split, transform each applicable submodel -or-
	# 				Apply transform to model
	# IV -- Solve using transformed model -or-
	#       solve for variance using transformed model, optimize -or-
	#				split model -or-
	#				nothing if missing data split already happened
	#	V -- If partial solve or split (MD or otherwise) call optimizer
	# VI -- Extract parameters and reinsert to named model
	# VII - Plug values matrix from named model back in to original model


setClass("PPMLSolveType",
	representation(
		result = "character",
		isMultiLayer = "logical",
		isMatrixSpecified = "logical",
		hasNHEV = "logical",
		Cerr = "matrix",
		relevantConstraints = "vector",
		hasMissingness = "logical",
		hasNoLatentCovariances = "logical",
		latentCovNotSaturated = "logical",
		hasFixedExpectedLatentMean = "logical",
		manifestVars = "vector",
		latentVars = "vector",
		fakeLatents = "vector"
	),
	prototype(
		result="Check",
		isMultiLayer = FALSE,
		isMatrixSpecified = FALSE,
		hasNHEV = FALSE,
		Cerr = NULL,
		relevantConstraints = NULL,
		hasMissingness=FALSE,
		hasNoLatentCovariances = FALSE,
		latentCovNotSaturated = FALSE,
		hasFixedExpectedLatentMean = FALSE,
		manifestVars = character(0),
		latentVars = character(0),
		fakeLatents = character(0)
	)
)

##' imxPPML
##'
##' Potentially enable the PPML optimization for the given model.
##'
##' @param model the MxModel to evaluate
##' @param flag whether to potentially enable PPML
imxPPML <- function(model, flag=TRUE) {
	if (!(isS4(model) && is(model, "MxModel"))) {
		stop("Argument 'model' must be MxModel object")
	}
	if (!(length(flag) == 1 && is.logical(flag) && !is.na(flag))) {
		stop("Argument 'flag' must be TRUE or FALSE")
	}
	# Expectation exists / must be a RAM expectation
	expectation <- model$expectation
	if(is.null(expectation) || !is(expectation, "MxExpectationRAM")) {
		return(NA)
	}
  if (any(expectation$isProductNode)) {
    stop("Cannot combine latent products and PPML")
  }
	if (flag) {
		model@options$UsePPML <- "Yes"
	} else {
		model@options$UsePPML <- "No"
	}
	return(model)
}


single.na <- function(a) {
    return((length(a) == 1) &&
        (is.list(a) || is.vector(a) || is.matrix(a)) &&
        (is.na(a) == TRUE))
}

PPMLTransformModel <- function(model.original) {
	# Name anonymous parameters in model
	pair <- (omxNameAnonymousParameters(model.original))
	model.named <- pair[[1]]

	solveType <- PPML.CheckApplicable(model.named)


	# PPML not applicable to model
	if (single.na(solveType))
	{
		model.original@options$UsePPML <- "Inapplicable"
		return(model.original)
	}

	model.pretransformed <- model.named

	# Apply pretransforms to model

	# Remove changes made to data by the mxRun function
	if (!is.null(model.pretransformed$data@means))
		model.pretransformed@data <- mxData(numObs=model.pretransformed@data@numObs, observed=model.pretransformed@data@observed, type=model.pretransformed@data@type, means=as.vector(model.pretransformed@data@means))



	# Fold in fakelatents
	if (length(solveType@fakeLatents))
	{
		model.pretransformed <- PPML.Pre.FakeLatents(model.pretransformed, solveType)
		solveType@latentVars <- setdiff(solveType@latentVars, solveType@fakeLatents)
		# If matrix specified, specified by indices -- need to refigure manifestVars and latentVars in solveType
		if ( is.numeric(solveType@latentVars) && is.numeric(solveType@manifestVars) )
		{
			# Iterate through F matrix to find which columns are manifests (1 present) and
			# which columns are latents (no 1 present in column)
			Fmatrix <- model.pretransformed[[model.pretransformed$expectation@F]]
			manifestVars <- numeric(0)
			latentVars <- numeric(0)
			for (i in 1:dim(Fmatrix@values)[[2]]) {
				if (any(as.logical(Fmatrix@values[,i])))
					manifestVars <- c(manifestVars, as.numeric(i))
				else
					latentVars <- c(latentVars, as.numeric(i))
			}
			solveType@manifestVars <- manifestVars
			solveType@latentVars <- latentVars
		}
	}


	model.noFakeLatents <- model.pretransformed
	# Build Cerr (with nonfakelatented model)
	# Also detects if the model is not a valid PPML NHEV model
	# Aborts PPML transform if so

	solveType <- PPML.Pre.UpdateSolveType(model.noFakeLatents, solveType)

	if (single.na(solveType))
	{
		model.original@options$UsePPML <- "Inapplicable"
		return(model.original)
	}


	# Fix NHEV
	if (solveType@hasNHEV)
	{

		# Fix up the model
		model.pretransformed <- PPML.Pre.FixNHEV(model.pretransformed, solveType)
	}


	# Apply transforms to model
	if (solveType@hasMissingness)
	{
		# Missing Data Case
		model.transformed <- PPMLMissingData(model.pretransformed, solveType)

		# Check to make sure PPML is applicable to model (shouldn't ever happen here)
		if (single.na(model.transformed))
		{
			model.original@options$UsePPML <- "Inapplicable"
			return(model.original)
		}

		model.transformed@options$UsePPML <- "No"
		results <- mxRun(model.transformed)
	}
	else
	{
		pair <- PPML.Transform(model.pretransformed, solveType)

		model.transformed <- pair[[1]]
		lambda <- pair[[2]]

		if (solveType@result == "PartialSolve" || solveType@result == "Solve")
			model.transformed <- PPML.SolveOrPartialSolve(model.transformed, lambda, solveType)

		if (solveType@result == "PartialSolve" )
		{

			model.transformed@options$UsePPML <- "No"
			# Optimize partially solved
			results <- mxRun(model.transformed)

			# Restore free values
			results <- omxSetParameters(results, labels=names(omxGetParameters(model.named)), free=TRUE)
		}
		else if (solveType@result == "Split")
		{
			# Split the model if necessary
			model.transformed <- PPML.Split(model.transformed, solveType)
			model.transformed@options$UsePPML <- "No"

			# Optimize the split model
			results <- mxRun(model.transformed)
		}
		else
			results <- model.transformed
	}

	if (solveType@hasNHEV)
	{
		results <- PPML.Post.UnfoldNHEV(results, model.noFakeLatents, solveType)
	}





	# Extract parameters and reinsert to named model
	params <- omxGetParameters(results)
	model.named <- omxSetParameters(model.named, names(params), values=as.vector(params))


	# Plug values matrix from named model back in to original model
	model.original[[model.original$expectation@S]]@values <- model.named[[model.original$expectation@S]]@values
	if (!single.na(model.original$expectation@M))
		model.original[[model.original$expectation@M]]@values <- model.named[[model.original$expectation@M]]@values

	# Preserve output data and indicate what type of solution was used
#	model.original@output <- results@output
	model.original@options$UsePPML <- solveType@result

	# Return original model with solution
	return(model.original)
}




PPML.CheckApplicable <- function(model) {
	solveType = new("PPMLSolveType")


	# Explicitly, don't use PPML -OR- no explicit instruction to use PPML and default is no
	if (model@options$UsePPML == "No" || (is.null(model@options$UsePPML) && getOption("mxOptions")$UsePPML == "No"))
	{
		#print("PPML abort: disabled")
		return(NA)
	}

	#is the model a RAM model?
	# NOTE: This could be made redundant by implementing the transform call
	# from the MxExpectationRAM function
	expectation <- model$expectation
	if(is.null(expectation) || !is(expectation, "MxExpectationRAM")) {
		#print("PPML abort: Not a RAM model")
		return(NA)
	}

	# Extract RAM matrices
	Aname <- expectation@A
	Sname <- expectation@S
	Fname <- expectation@F
	Mname <- expectation@M
	Amatrix <- model[[Aname]]
	Smatrix <- model[[Sname]]
	Fmatrix <- model[[Fname]]
	Mmatrix <- NULL
	if (!single.na(Mname)) {
		Mmatrix <- model[[Mname]]
	}
	constraints <- model@constraints

	# Matrices must be MxMatrices
	if(!is(Amatrix, "MxMatrix") || !is(Smatrix, "MxMatrix") || !is(Fmatrix, "MxMatrix") || (!single.na(Mname) && !is(Mmatrix, "MxMatrix"))) {
		#print("PPML abort: Matrices not MxMatrices")
		return(NA)
	}

	# Make sure data is present, and raw or covariance
	if( !is.null(model$data) ) {
		if ( !(model$data@type == "raw" || model$data@type == "cov") )  {
			#print("PPML abort: Need raw or covariance matrix data")
			return(NA)
		}
	} else {
		#print("PPML abort: Model has no data")
		return(NA)
	}

	# Structure matrix must be fixed
	# TODO: Optimization possible for cases where only part of the structure matrix is fixed
	if(any(Amatrix@free)) {
		#print("PPML abort: Structure matrix not fixed")
		return(NA)
	}


	# If the model is matrix specified, uses numeric indices instead of dimnames
	# Then for split models: splits the expectation function in the split section
	manifestVars <- NULL
	latentVars <- NULL

	solveType@isMatrixSpecified <- length(model@manifestVars) == 0 && length(model@latentVars) == 0
	if (solveType@isMatrixSpecified) {

		if (length(model$expectation@dims) == 1 && single.na(unique(model$expectation@dims)))	# no dims anywhere NOTE: is this necessary?
		{
			#print("PPML abort: Model is missing dims")
			return(NA)
		}

		# Iterate through F matrix to find which columns are manifests (1 present) and
		# which columns are latents (no 1 present in column)
		for (i in 1:dim(Fmatrix@values)[[2]]) {
			if (any(as.logical(Fmatrix@values[,i])))
				manifestVars <- c(manifestVars, as.numeric(i))
			else
				latentVars <- c(latentVars, as.numeric(i))
		}
	}
	else
	{
		manifestVars <- model@manifestVars
		latentVars <- model@latentVars
	}

	# Put manifestVars, latentVars in to solveType
	solveType@manifestVars <- manifestVars
	solveType@latentVars <- latentVars

	### Call latent classifier function to classify latents
	classifiedLatents <- PPMLClassifyLatents(Amatrix, Smatrix, latentVars)
	if (single.na(classifiedLatents))
	{
		#print("PPML abort: Latents of improper form")
		return(NA)
	}
	realLatents <- classifiedLatents[[1]]
	fakeLatents <- classifiedLatents[[2]]
	solveType@fakeLatents <- fakeLatents
	rootLatents <- classifiedLatents[[3]]
	nonRootLatents <- classifiedLatents[[4]]


	# Check if expected means vector for latents are FREE
	# If all are not free, then solution doesn't apply
	# Use solution as best guess, but don't fix them for partial solution
	# If all are not free, analytical solution becomes partial solution
	if (!single.na(Mname))
	{
		if (!all(Mmatrix@free[,realLatents]))
			solveType@hasFixedExpectedLatentMean <- TRUE
	}

	# DEVELOPMENT -- Some of these models should be transformable
	# TODO: Multilayered models
	if (length(nonRootLatents) > 0) {
		solveType@isMultiLayer <- TRUE
	}

	# (I-Lambda)^-1 -- Structure matrix must be invertible
	# Check for multi-layered models
	# PROFILING: Expensive
#	if ( det( diag(dim(Amatrix@values)[[1]]) - Amatrix@values ) == 0 ) {
#		return(NA)
#	}

	# BASIC: Model must have manifestvars and latentvars and more manifests than latents
	if (length(realLatents) == 0 || length(manifestVars) == 0 ||
		length(realLatents) >= length(manifestVars)) {
		#print("PPML abort: Must have manifests, latents, and more manifests than latents")
		return(NA)
	}

	# DATA MISSINGNESS CHECK
	if (model$data@type == "raw" && any(is.na(model$data@observed))) {
		# Check missingness patterns: if none of the patterns have more manifests than latents,
		# then PPML cannot be applied at all
		if ( (length(manifestVars) - min(apply(is.na(model$data@observed),1,sum))) <= length(realLatents) ) {
			#print("PPML abort: No missingness patterns are transformable")
			return(NA)
		}

		# Might be solvable, but for now, split only
		solveType@hasMissingness <- TRUE
		#solveType <- "Split"
	}

	#  Partial Solve possible when all covariances between latents are fixed to zero but all
	# variances of the latents are free
	#  Analytical solution still possible for cases with one latent
	#  All covariances are fixed, zero
	# Conditional below:
	#  More than one latent
	#  There are no free covariances between the latents
	hasNoFreeCovariances <- !any(Smatrix@free[realLatents, realLatents] & !diag(TRUE, length(realLatents), length(realLatents)) )
	#  All latent covariances are (effectively) zero
	allLatentCovsZero <- all( abs(Smatrix@values[realLatents, realLatents] - diag(diag(Smatrix@values[realLatents,realLatents]))) < .001 )

	if ( hasNoFreeCovariances && allLatentCovsZero ) {

		if ( all(as.logical(diag(Smatrix@free[realLatents, realLatents]))) ) {
			# All latent variances are free
			#if (solveType != "Split") solveType <- "PartialSolve"
			solveType@hasNoLatentCovariances <- TRUE
		} else {
				# One or more latent variance is fixed
				#print("PPML abort: One or more latent variance is fixed")
				#return(NA)
				solveType@latentCovNotSaturated <- TRUE

		}

	} else if ( !all(Smatrix@free[realLatents, realLatents]) ) {
#		# If some less structured combination of variances and covariances of the latents
#		# is fixed, probably not applicable
#		print("PPML abort: Inapplicable variance/covariance structure (Fixedness)")
#		return(NA)
		solveType@latentCovNotSaturated <- TRUE
	}

	# Multilayer doesn't work with missingness
	if (solveType@isMultiLayer && solveType@hasMissingness)
		return(NA)

	# Determine solveType results
	if (!solveType@hasMissingness) {
		# No data missingness
		if ((solveType@hasNoLatentCovariances || solveType@latentCovNotSaturated) && (length(latentVars) - length(fakeLatents)) > 1) {
			solveType@result <- "Split"
		} else {
			if (solveType@hasFixedExpectedLatentMean)
				solveType@result <- "PartialSolve"
			else
				solveType@result <- "Solve"
		}
	} else {
		# Data missingness
		solveType@result <- "MissingData"
	}

	# Although not explicit in the code,
	# solveType <- "Solve" happens implicitly when Smatrix@free[latentVars, latentVars] is all free
	# (saturated covariance matrix)

	# BROKEN CASE: Covariance data + means, not analytical solution
	# BLOCK THIS CASE
	# Covariance data
	# Means data present
	# Solvetype is not "Solve" == analytical soln
	if (model$data@type == "cov" && !single.na(model$data@means) && solveType@result != "Solve")
		return(NA)

	return(solveType)

}

PPML.Check.UseOptimizer <- function(opt)
{
		if (is.null(opt))
			return(TRUE)
		if (!(opt == "No" || opt == "Inapplicable"))
			return(FALSE)
		return(TRUE)
}


PPML.Pre.UpdateSolveType <- function(model, solveType)
{
	manifestVars <- solveType@manifestVars
	latentVars <- solveType@latentVars

	# Extract RAM matrices
	expectation <- model$expectation
	Aname <- expectation@A
	Sname <- expectation@S
	Fname <- expectation@F
	Mname <- expectation@M
	Amatrix <- model[[Aname]]
	Smatrix <- model[[Sname]]
	Fmatrix <- model[[Fname]]
	Mmatrix <- NULL
	if (!single.na(Mname)) {
		Mmatrix <- model[[Mname]]
	}


	maybeNHEV <- FALSE

	# NHEV CHECK -- PART I
	# Error matrix := Smatrix[manifestVars, manifestVars]
	# With any variances in fake latents in the appropriate diagonal spots

	# TODO: In NHEV cases, constraints that aren't part of the error covariance matrix -- can PPML
	# be applied with any other constraint on the model, or will further constraints always break it?

	# Any free values off the diagonal in the manifest covariance matrix?
	hasFreeValuesOffDiagonal <- any(as.logical( Smatrix@free[manifestVars,manifestVars] & !diag(rep(TRUE, length(manifestVars))) ) )

	# If there are unlabeled params in the error matrix, not even an NHEV case
	# -- NHEV algorithm relies on constraints between labeled params
	# Can't do anything with this
	hasUnlabeledFreeParams <- !all(Smatrix@free[manifestVars,manifestVars] == !is.na(Smatrix@labels[manifestVars,manifestVars]))
	if (hasUnlabeledFreeParams) # IMPLICIT: >1 manifestVar, otherwise PPML not applicable anyways. SO the two must be constrained together somehow
	{
#		print("PPML abort: Unlabeled free error parameters")
		return(NA)
	}

	# Need constraints for NHEV, but can also break model if not relevant to error matrix
	hasConstraints <- length(model@constraints) > 0

	# Figure out if there's more than one label on the diagonals of the error matrix

	hasMultipleDiagErrorLabels <- length(unique(diag(Smatrix@labels[manifestVars, manifestVars]))) > 1

	# Actual checking
	# First case, non-diagonal matrix
	if (hasFreeValuesOffDiagonal) {
		# Need constraints for the error matrix to vary as matrixC*scalarVarE
		if (!hasConstraints)
		{
#			print("PPML abort: Unconstrained error variance")
			return(NA)
		}

		if (!hasMultipleDiagErrorLabels)
		{
			# Matrix diagonals are all equal
			# Need for the free values off the diagonal to be labeled differently
			# Otherwise you get Identity + additional 1s scattered in the matrix,
			# which are not (in general?) invertible
			diagLabels <- unique(diag(Smatrix@labels[manifestVars,manifestVars]))
			allLabels <- unique(as.vector(Smatrix@labels[manifestVars,manifestVars]))

			hasDifferentLabelsOffDiag <- as.logical(length(setdiff(allLabels, c(diagLabels,NA))))
			if (!hasDifferentLabelsOffDiag)
			{
#				print("PPML abort: Constraints make non-positive-definite error matrix")
				return(NA)
			}
		}

		maybeNHEV <- TRUE # Actually, right now, just "Maybe"
		# Second half of check happens after fakeLatents are folded in to model,
		# Partway through the pretransform phase
		# TODO: Maybe make a second parameter, maybeNHEV, for clarity?
	}
	# Second case, diagonal matrix
	else
	{
		if (hasMultipleDiagErrorLabels)
		{
			# Need constraints for the error matrix to vary as matrixC*scalarVarE
			if (!hasConstraints)
			{
#				print("PPML abort: Constraints required on error matrix for NHEV transform")
				return(NA)
			}
			maybeNHEV <- TRUE
		}
		# IMPLICIT else: Diagonal matrix where all error variances are constrained
		# to equality --> standard case, not NHEV

	}


	if (!maybeNHEV)
		return(solveType)


	hasIrrelevantConstraints <- FALSE

	# NHEV Check -- Part II
	# Generates the Cerr matrix
	# Leave this for latest possible in case the model is found to be PPML-inapplicable
	# This is possibly really slow, and should be avoided if at all possible

	constraints <- model@constraints

	# IMPORTANT: NHEV and Missingness algorithms do not seem to be compatible
	if (solveType@hasMissingness)
		return(NA)

	# Call buildCErr to build CErr matrix
	bCERetVal <- buildCErr(model[[model$expectation@S]], solveType@manifestVars, constraints)
	if (is.null(bCERetVal))
		return(NA)
	Cerr <- bCERetVal[[1]]
	relevantConstraints <- bCERetVal[[2]]

	hasIrrelevantConstraints <- length(relevantConstraints) < length(constraints)
	# DEVELOPMENT
	# If there are irrelevant constraints, analytical solution is probably wrong
	# Might as well try it?
	# TODO: See if this breaks any models
	# /DEVELOPMENT
	if (hasIrrelevantConstraints)
		solveType@result <- "Split" # Downgrade solveType to split
	# IMPLICIT ELSE: leaves solveType as it was before (NHEV will never improve it)

	# buildCErr returns null if there's an invalid constraint
	if (is.null(Cerr))
		return(NA)

	# Noninvertible Cerr
	# --> Not a valid PPML model, need to be able to invert Cerr
	if (det(Cerr) == 0)
		return(NA)

	solveType@Cerr <- Cerr
	solveType@relevantConstraints <- relevantConstraints
	solveType@hasNHEV <- TRUE

	return(solveType)

}








PPMLClassifyLatents <- function(Amatrix, Smatrix, latentVars) {
	# Classify latents
	fakeLatents <- array(0,0)
	rootLatents <- array(0,0)
	for(latentVar in latentVars){
		# Get nonzero loadings from the asymmetric matrix
		loads <- which(Amatrix@values[,latentVar] != 0)

		# Fake latents only load on to one manifest variable
		if ( length(loads) == 1) {
			# Load must have value 1
			# Otherwise, folding fakelatent back out becomes difficult and less graceful methods are required
			if (Amatrix@values[loads[1], latentVar] == 1) {
				# All loadings from the latent must be on manifestVars
				#  NOTE: Latents that covary with other latents but have no loadings on to anything?
				#  Lambda is probably singular in these cases.
				if (length(which(Amatrix@values[latentVars, latentVar] != 0)) == 0) { # No loadings on latentVars
					# Latent must not covary with any manifests or other latents
					if ( Smatrix@free[latentVar,latentVar] && length(which(Smatrix@free[,latentVar])) == 1) {
						# All of these manifestVars must have no inherent error variances /
						# loads are all on to manifests without their own error variances
						if ( !Smatrix@free[loads, loads] && Smatrix@values[loads, loads] == 0 ) {
							fakeLatents <- append(fakeLatents,latentVar)
							next; # Root and Fake categories are mutually exclusive (explicitly, not by property; this wouldn't work without this code.)
						}
					}
				}
			}
		}

		# Check if it's a root latent - no loadings from other latents
		inLoads = which(Amatrix@values[latentVar, latentVars] != 0)
		if (length(inLoads) == 0) {
			rootLatents <- append(rootLatents, latentVar)
		}
	}
	realLatents <- latentVars[is.na(pmatch(latentVars, fakeLatents))]
	nonRootLatents <- latentVars[is.na(pmatch(latentVars, c(fakeLatents, rootLatents)))]

	return(list(realLatents, fakeLatents, rootLatents, nonRootLatents))
}




PPML.Pre.FakeLatents <- function(model, solveType)
{
	Aname <- model$expectation@A
	Sname <- model$expectation@S
	Fname <- model$expectation@F
	Mname <- model$expectation@M

	Amatrix <- model[[Aname]]
	Smatrix <- model[[Sname]]
	Fmatrix <- model[[Fname]]
	Mmatrix <- NULL
	if (!single.na(Mname)) {
		Mmatrix <- model[[Mname]]
	}

	latentVars <- solveType@latentVars
	manifestVars <- solveType@manifestVars
	fakeLatents <- solveType@fakeLatents

	# Rebuild the model, folding fakeLatents in to the S matrix
	for (fakeLatent in fakeLatents) {
		forWhich <- which(Amatrix@values[ ,fakeLatent] != 0) # Manifest to which the fakeLatent corresponds
		loadVal <- Amatrix@values[forWhich]

		Smatrix@values[forWhich, forWhich] <- Smatrix@values[fakeLatent,fakeLatent] # Move starting value
		Smatrix@free[forWhich, forWhich] <- TRUE # The manifest is now free
		Smatrix@labels[forWhich, forWhich] <- Smatrix@labels[fakeLatent,fakeLatent] # Give the manifest the fakeLatent's label

	}

	# Remove fakeLatents from A, S, and F matrices
	remainingVars <- c(manifestVars, setdiff(latentVars, fakeLatents))

	# Get remainingVars in correct order
	if (solveType@isMatrixSpecified)
	{
		remainingVars <- sort(remainingVars)
	}
	else
	{
		sortedVars <- c()
		vars <- colnames(Fmatrix)
		for (var in vars)
		{
			if (any(remainingVars == var))
				sortedVars <- c(sortedVars, var)
		}
		remainingVars <- sortedVars
	}

	Amatrix <- Amatrix[remainingVars, remainingVars]
	Smatrix <- Smatrix[remainingVars, remainingVars]
	Fmatrix <- Fmatrix[ ,remainingVars]
	if (!single.na(Mname)) {
		Mmatrix <- Mmatrix[,remainingVars]
	}

	model[[Aname]] <- Amatrix
	model[[Sname]] <- Smatrix
	model[[Fname]] <- Fmatrix
	if (!single.na(Mname))
		model[[Mname]] <- Mmatrix

	# Fix expectation function/latent variable list
	if (!is.numeric(latentVars)) { # Path-specified
		model@latentVars <- setdiff(latentVars, fakeLatents)
	} else { # Matrix-specified
		# Repair dims
		model$expectation@dims <- model$expectation@dims[-fakeLatents]
	}

	return(model)

}




PPML.Pre.FixNHEV <- function(model, solveType) {
	Aname <- model$expectation@A
	Sname <- model$expectation@S
	Mname <- model$expectation@M

	Amatrix <- model[[Aname]]
	Smatrix <- model[[Sname]]
	Mmatrix <- NULL
	if (!single.na(Mname)) {
		Mmatrix <- model[[Mname]]
	}

	constraints <- model@constraints
	manifestVars <- solveType@manifestVars
	latentVars <- solveType@latentVars

	Cerr <- solveType@Cerr
	relevantConstraints <- solveType@relevantConstraints


	# Find transformation matrix R^-1
	Rinv <- (solve(chol(Cerr)))

	# Apply it to original Cerror to get transformed C'error
	# (Actually, just generate appropriately sized identity matrix)
	CerrPrime <- diag(rep(1, dim(Cerr)[1]))

	Smatrix@values[manifestVars, manifestVars] <- CerrPrime # Reinsert to Smatrix

	# Set all labels for the errors to the same label
	Smatrix@free[manifestVars, manifestVars] <- FALSE
	Smatrix@labels[manifestVars, manifestVars] <- NA
	for (manifestVar in manifestVars) {
		if (Smatrix@values[manifestVar, manifestVar] != 0) {
			Smatrix@labels[manifestVar, manifestVar] <- '_PPML_NHEV_ErrParam' #unique(clabels)
			Smatrix@free[manifestVar, manifestVar] <- TRUE # NOTE: ? maybe
		}
	}

	# Transform structure matrix
	Amatrix[manifestVars, latentVars]@values <- t(Rinv) %*% Amatrix[manifestVars, latentVars]@values

	# Reinsert transformed matrices to model
	model[[Aname]] <- Amatrix
	model[[Sname]] <- Smatrix

	# TODO: Is there a missing transform on the M matrix?
	if (!single.na(Mname))
		model[[Mname]] <- Mmatrix

	# Remove relevant constraints
	for (relevantConstraint in relevantConstraints) {
		constraints <- setdiff(constraints, list(model@constraints[[relevantConstraint]]))
	}

	model@constraints <- constraints

	# Transform data
	if (model$data@type == "raw")
	{
		# Transform raw data
		model$data@observed <- as.matrix(model$data@observed) %*% (Rinv)
		if (!is.numeric(manifestVars))
			colnames(model$data@observed) <- manifestVars
		else {
			colnames(model$data@observed) <- model$expectation@dims[manifestVars]
		}

	}
	else if (model$data@type == "cov")
	{
		# Transform variance data
		model$data@observed <- t(Rinv) %*% as.matrix(model$data@observed) %*% (Rinv)

		if (!single.na(model$data@means))
			model$data@means <- as.matrix(model$data@means) %*% (Rinv)

		# Restore dimnames
		if (!(is.numeric(manifestVars) && is.numeric(latentVars))) {
			# For path-specified models:
			colnames(model$data@observed) <- manifestVars
			rownames(model$data@observed) <- manifestVars
			if (!single.na(model$data@means))
				colnames(model$data@means)    <- manifestVars
		} else {
			# For matrix-specified models
			colnames(model$data@observed) <- model$expectation@dims[manifestVars]
			rownames(model$data@observed) <- model$expectation@dims[manifestVars]
			if (!single.na(model$data@means))
				colnames(model$data@means)    <- model$expectation@dims[manifestVars]
		}
	}
	return(model)
}




buildCErr <- function(Smatrix, manifestVars, constraints) {
	return(NULL)
}

PPML.Post.UnfoldNHEV <- function(result, model.noFakeLatents, solveType)
{
	Sname <- model.noFakeLatents$expectation@S
	manifestVars <- solveType@manifestVars

	# Extract solved error param
	errParam <- omxGetParameters(result)[['_PPML_NHEV_ErrParam']]

	# Scale Cerr matrix appropriately
	scaledCerr <- solveType@Cerr * errParam

	# Pull out labels, free associated with Cerr matrix
	labelMatrix <- model.noFakeLatents[[Sname]]@labels[manifestVars,manifestVars]
	freeMatrix <- model.noFakeLatents[[Sname]]@free[manifestVars,manifestVars]

	# Put labels, free, scaled Cerr back in to result
	result[[Sname]]@values[manifestVars,manifestVars] <- scaledCerr
	result[[Sname]]@labels[manifestVars,manifestVars] <- labelMatrix
	result[[Sname]]@free[manifestVars,manifestVars] <- freeMatrix

	return(result)

}


PPMLMissingData <- function(model, solveType) {
	# Need to name anonymous params to constrain across the submodels
	Aname <- model$expectation@A
	Sname <- model$expectation@S
	Fname <- model$expectation@F
	Mname <- model$expectation@M



	latMeanVarNames <- character(0)
	if (!single.na(Mname))
	{
		latMeanVarNames <- model[[Mname]]@labels[,solveType@latentVars]
		# Strip out NA (occurs when some latent means are fixed, not free anyways)
		latMeanVarNames <- latMeanVarNames[ which(!is.na(latMeanVarNames)) ]
	}

	errVarName <- diag(model[[Sname]]@labels[solveType@manifestVars, solveType@manifestVars])[[1]]

	# Check through model$data@observed
	# Find unique missingness patterns
	patterns <- as.list(data.frame(t(unique(is.na(model$data@observed)))))
	patternLocs <- list()
	patternCounts <- list()
	for ( patt in patterns) {
		patternLocs <- append(patternLocs, list(which(apply(is.na(model$data@observed), 1, function(row) { if (all(row == patt)) TRUE else FALSE } ))))
	}

	# Create a submodel for each missingness pattern with the appropriate
	# manifest variables removed
	bigModel <- mxModel(paste("(PPML Missing Data)", model@name))
	submodelNames <- c()
	PPMLAppliedCount <- 0
	PPMLSolvedCount <- 0

	meanVarE <- 0
	meanLatentMeans <- numeric(0)
	if (!single.na(Mname))
		meanLatentMeans <- vector(mode="numeric", length=length(latMeanVarNames))

	for (m in 1:length(patterns)) {
		newSubmodel <- mxModel(model)
		newSolveType <- solveType

		# Path specified versus matrix specified
		# Get label sets from data dimnames using LIVs
		removedMans <- (dimnames(newSubmodel$data@observed)[[2]])[patterns[[m]]]

		remainingMans <- (dimnames(newSubmodel$data@observed)[[2]])[!patterns[[m]]]
		remainingVars <- NULL # get remainingVars in scope
		remainingMansData <- remainingMans
		# NOTE: actually only need removedMans?
		if ( solveType@isMatrixSpecified ) {
			remainingVars <- c(remainingMans, model$expectation@dims[solveType@latentVars])
			# Matrix specified - Use dims from expectation to get location vector
			removedMansLoc <- c()
			for (removedMan in removedMans) {
				removedMansLoc <- c(removedMansLoc, which(model$expectation@dims == removedMan))
			}
			if (length(removedMans)) removedMans <- sort(removedMansLoc)

			remainingMansLoc <- c()
			for (remainingMan in remainingMans) {
				remainingMansLoc <- c(remainingMansLoc, which(model$expectation@dims == remainingMan))
			}
			remainingMans <- sort(remainingMansLoc)

			remainingVarsLoc <- c()
			for (remainingVar in remainingVars) {
				remainingVarsLoc <- c(remainingVarsLoc, which(model$expectation@dims == remainingVar))
			}
			remainingVars <- sort(remainingVarsLoc)
		} else {

			remainingVars <- c(remainingMans, newSolveType@latentVars)
		}

		# If any manifests are missing from the data, remove those manifests from the submodel
		if (!is.null(removedMans))
		{
			# Fix matrices
			newSubmodel[[Aname]] <- newSubmodel[[Aname]][remainingVars, remainingVars]
			newSubmodel[[Sname]] <- newSubmodel[[Sname]][remainingVars, remainingVars]
			newSubmodel[[Fname]] <- newSubmodel[[Fname]][unlist(apply(newSubmodel[[Fname]]@values[ , remainingVars], 2, function(r) which(as.logical(r)))), remainingVars]
			if (!single.na(Mname)) {
				newSubmodel[[Mname]] <- newSubmodel[[Mname]][ , remainingVars]
			}

			if (!single.na(newSubmodel$data@means)) {
				newSubmodel$data@means <- newSubmodel$data@means[, remainingMans]
			}

			# Fix list of manifestVars
			if ( !solveType@isMatrixSpecified ) {
				# PATH
				newSubmodel@manifestVars <- remainingMansData
				newSolveType@manifestVars <- remainingMansData
			} else {
				# MATRIX
				dimIndices <- sort(c(remainingMans, solveType@latentVars))
				newSubmodel$expectation@dims <- newSubmodel$expectation@dims[dimIndices]
				newSolveType@manifestVars <- remainingMans
				# Need to fix up indices, as items have potentially been removed, shifting indices
				for (remVar in rev(sort(removedMans)))
				{
					# Decrement indices > remVar by one
					newSolveType@manifestVars <- unlist(lapply(newSolveType@manifestVars, function (x) { if (x > remVar) return(x-1) else return(x) } ))
					newSolveType@latentVars <- unlist(lapply(newSolveType@latentVars, function (x) { if (x > remVar) return(x-1) else return(x) } ))
				}

			}
		}

		# Remove pattern-appropriate manifests in data, including means vectors
		# NOTE: Kludgy fix for the data matrix turning in to a vector when there's only one
		# manifest remaining -- should this actually happen?
		if (length(remainingMans) == 1 ) {
			newSubmodel$data@observed <- as.matrix(newSubmodel$data@observed[patternLocs[[m]], remainingMansData])
			newSubmodel$data@numObs <- dim(newSubmodel$data@observed)[[1]]
			colnames(newSubmodel$data@observed) <- remainingMansData
		} else {
			# NORMAL CASE -- should probably be this all the time
			newSubmodel$data@observed <- newSubmodel$data@observed[patternLocs[[m]], remainingMansData]
			newSubmodel$data@numObs <- dim(newSubmodel$data@observed)[[1]]
			colnames(newSubmodel$data@observed) <- remainingMansData
		}


		# Rename each submodel
		newSubmodel@name <- paste('PPML_MG_Submodel', m, sep="")

		# Transform each of these submodels with PPML,
		# if applicable
		if (length(newSolveType@manifestVars) > length(newSolveType@latentVars))
		{
			pair <- PPML.Transform(newSubmodel, newSolveType)
			newSubmodel <- PPML.SolveOrPartialSolve(pair[[1]], pair[[2]], newSolveType)
			newSubmodel <- PPML.Split(newSubmodel, newSolveType)
		}
		submodelNames <- c(submodelNames, newSubmodel@name)

		# Check if PPML was applicable on the submodel
		if (!is.null(newSubmodel@options$UsePPML) &&
			(newSubmodel@options$UsePPML == "Solved" || newSubmodel@options$UsePPML == "PartialSolved" || newSubmodel@options$UsePPML == "Split")) {
			PPMLAppliedCount <- PPMLAppliedCount + 1

			# Below should be zero, as solution should not be applied to missing data submodels
			if (newSubmodel@options$UsePPML == "Solved")
				PPMLSolvedCount <- PPMLSolvedCount + 1

			# Track mean of the solved error variances to produce best estimate for starting point
			meanVarE <- meanVarE + omxGetParameters(newSubmodel)[errVarName]
			# Track mean of the solved latent means to produce best estimate for starting point
			if (!single.na(Mname))
			{
				latMeans <- omxGetParameters(newSubmodel)[latMeanVarNames]
				for (i in 1:length(latMeanVarNames))
				{
					latName <- latMeanVarNames[i]
					if (!single.na(meanLatentMeans[latName]))
						meanLatentMeans[i] <- meanLatentMeans[i] + meanLatentMeans[latName]
				}

				meanLatentMeans <- meanLatentMeans
			}

		}

		# Set UsePPML to "No" to allow optimization to occur
		newSubmodel@options$UsePPML <- "No"

		# Combine these submodels in to a larger model
		bigModel <- mxModel(bigModel, newSubmodel)
	}

	# Need to check if any of the submodels were successfully transformed; if not, reject transformation
	if (PPMLAppliedCount == 0) {
		return(NA)
	}

	meanVarE <- meanVarE/PPMLAppliedCount
	meanLatentMeans <- meanLatentMeans/PPMLAppliedCount
	bigModel <- omxSetParameters(bigModel, c(errVarName, latMeanVarNames), values=c(meanVarE,meanLatentMeans))

	# Objective = sum
	# TODO: Combine algebra objectives from PPML-transformed models?
	#objectives <- paste(submodelNames, "objective", sep = ".")
	objectives <- paste(submodelNames, "fitfunction", sep = ".")
	objectives <- paste(objectives, collapse = " + ")
	expression <- paste("mxAlgebra(", objectives, ", name = 'SumObjective')", sep = "")
	algebra <- eval(parse(text=expression))
	objective <- mxFitFunctionAlgebra("SumObjective")
	bigModel <- mxModel(bigModel, objective, algebra)
	bigModel <- mxOption(bigModel, "UsePPML", "MissingData")


	return(bigModel)
}





PPML.Transform <- function(model, solveType)
{
	Aname <- model$expectation@A
	Sname <- model$expectation@S
	Fname <- model$expectation@F
	Mname <- model$expectation@M

	Amatrix <- model[[Aname]]
	Smatrix <- model[[Sname]]
	Fmatrix <- model[[Fname]]
	Mmatrix <- NULL
	if (!single.na(Mname)) {
		Mmatrix <- model[[Mname]]
	}

	manifestVars <- solveType@manifestVars
	latentVars <- solveType@latentVars

	#transform A to E
	E <- solve(diag(nrow(Amatrix)) - Amatrix@values)
	#select only these columns of A which are real latents
	#get loadings matrix:= The part of A which goes from the latents to the manifests
	lambda <- as.matrix(E[manifestVars, latentVars])

	qrDecom <- qr(lambda)

	#check if loadings matrix is of full rank
	k <- length(latentVars)
	#if (qrDecom$rank != dim(lambda)[2] || k != qrDecom$rank){
	#	return(model)
	#}
	#last check passed, can start modifying model

	#calculate upper triangle matrix
	#orthogonal
	Q <- t(qr.Q(qrDecom, complete = TRUE))

	#transform loadings matrix
	lambda <- Q %*% lambda
	#set all rows from k+1 to zero
	lambda[(k + 1) : nrow(lambda), ] <- 0
	#insert new loadings matrix in A
	Amatrix@values[manifestVars,latentVars] <- lambda
	Amatrix@values[latentVars,latentVars] <- 0 # For latents predicting latents


	# Reinsert transformed matrices back in to model
	model[[Aname]] <- Amatrix
	model[[Sname]] <- Smatrix
	model[[Fname]] <- Fmatrix
	if (!single.na(Mname)) {
		model[[Mname]] <- Mmatrix
	}

	#transform data or cov
	if(model$data@type == "raw") {
		model$data@observed <- as.matrix(model$data@observed) %*% t(Q)

		# Restore dimnames
		if (!is.numeric(manifestVars))
			colnames(model$data@observed) <- manifestVars
		else {
			colnames(model$data@observed) <- model$expectation@dims[manifestVars]
		}
	}
	else if(model$data@type == "cov") {
		# Transform variance data
		model$data@observed <- (Q) %*% as.matrix(model$data@observed) %*% t(Q)

		# Restore dimnames
		if (!solveType@isMatrixSpecified) {
			# For path-specified models:
			colnames(model$data@observed) <- manifestVars
			rownames(model$data@observed) <- manifestVars
		} else {
			# For matrix-specified models
			colnames(model$data@observed) <- model@expectation@dims[manifestVars]
			rownames(model$data@observed) <- model@expectation@dims[manifestVars]
		}

		# Transform means data, if it exists
		if (!single.na(model$data@means)){
			model$data@means <- as.matrix(model$data@means) %*% t(Q)
			# Restore dimnames
			if (!solveType@isMatrixSpecified)
				colnames(model$data@means) <- manifestVars	# Path-specified
			else
				colnames(model$data@means) <- model$expectation@dims[manifestVars] # Matrix-specified
		}

	}

	return(list(model, lambda))

}


PPML.SolveOrPartialSolve <- function(model, lambda, solveType)
{
	# Extract matrices from model
	expectation <- model$expectation

	Aname <- expectation@A
	Sname <- expectation@S
	Fname <- expectation@F
	Mname <- expectation@M
	Amatrix <- model[[Aname]]
	Smatrix <- model[[Sname]]
	Fmatrix <- model[[Fname]]
	Mmatrix <- NULL
	if (!single.na(Mname)) {
		Mmatrix <- model[[Mname]]
	}

	# Make option "Solve" or "PartialSolve"
	model@options$UsePPML <- solveType@result

	manifestVars <- solveType@manifestVars
	latentVars <- solveType@latentVars


	# Analytical solution for error variance
	# For raw data
	if (model$data@type == 'raw') {
		# Calculate varE
		K <- length(latentVars)
		M <- dim(model$data@observed)[[2]]
		N <- dim(model$data@observed)[[1]]
		varE <- sum(model$data@observed[ ,(K+1):M]^2 )
		varE <- varE / ( (M - K) * N )
	}
	else if (model$data@type == 'cov')
	{
		# Two components to the error variance in covariance data models:
		# The (post-transform) variances of the variables in the error model...
		K <- length(latentVars)
		M <- dim(model$data@observed)[[1]]
		varE <- sum(diag(as.matrix(model$data@observed[(K+1):M, (K+1):M]))) / (M-K)
		# And the sum of the square of the (post-transform) means of those variables
		if (!single.na(model$data@means))
		{
			# Need correction of N/(N-1) -- trying to generate sample variance, not population
			# For the variances pulled out of the transformed data cov matrix, this has been accounted for
			# However, denominator of means is N, not N-1
			N <- model$data@numObs
			varE <- varE + sum(model$data@means[,(K+1):M]^2) / (M-K) * N/(N-1)
		}
	}


	# Calculate lambdaInv
	lambdaInv <- solve(lambda[1:length(latentVars), ])

	# Analytical solution for means of latents
	if (!is.null(Mmatrix)) {
		muLatent <- NULL
		# Extract untransformed means
		if (model$data@type == 'raw') {
			# Calculate means of the manifest vars
			muLatent <- apply(as.matrix(model$data@observed[, 1:length(latentVars)]), 2, mean)
		} else if (model$data@type == 'cov') {
			# Manifest means are already specified
			muLatent <- model$data@means[1:length(latentVars)]
		}

		# Apply transform to means
		solvedM <- lambdaInv %*% muLatent
		if ( solveType@isMatrixSpecified ) {

			# If any of the latent means are fixed, can't just blindly put in the solved values
			# So, check:
			if (solveType@hasFixedExpectedLatentMean)
			{
				# Don't overwrite value of fixed latent
				for (i in 1:length(solveType@latentVars))
				{
					latentVar <- solveType@latentVars[i]
					if (Mmatrix@free[latentVar])
						Mmatrix@values[latentVar] <- solvedM[i]
				}
			}
			else
				Mmatrix@values[latentVars] <- solvedM

		} else {
			# For some reason, indexing by character dimnames doesn't work here
			# fix: lapply call in the index converts dimnames for latentvars in to appropriate numerical indices
			latentIndices <- unlist(lapply(latentVars, function(x) { which(dimnames(Mmatrix)[[2]] == x) }))

			# If any of the latent means are fixed, can't just blindly put in the solved values
			# So, check:
			if (solveType@hasFixedExpectedLatentMean)
			{
				# If some of them are fixed, iterate through, checking each one
				# If it's not fixed, insert the solved value as a best guess
				for (i in 1:length(latentIndices))
				{
					latentIndex <- latentIndices[i]
					if (Mmatrix@free[latentIndex])
						Mmatrix@values[latentIndex] <- solvedM[i]
				}
			}
			else
				Mmatrix@values[ latentIndices ] <- lambdaInv %*% muLatent

		}


		# Analytical solution for the means:
		# Fix means values so optimizer doesn't bother with them (if partialsolved; if free, don't bother)
		# -- Don't fix these if, in the original model, any of the expected latent means are not free to vary
		# -- Don't fix in MissingData case, as each solved submodel will have a different solution
		# 	-- Just plug them in, use the average as a best guess, and optimize simultaneously
		if (solveType@result == "PartialSolve" && !solveType@hasFixedExpectedLatentMean)
			Mmatrix@free[unlist(lapply(latentVars, function(x) { which(dimnames(Mmatrix)[[2]] == x) }))] <- FALSE

		# Insert Mmatrix back in to transformed model
		model[[Mname]] <- Mmatrix
	}


	if (solveType@result == "PartialSolve" || solveType@result == "MissingData")
	{
		# Reinsert calculated varE in to Smatrix, fix values
		diag(Smatrix@values[manifestVars, manifestVars]) <- rep(varE, length(manifestVars))

		if (solveType@result == "PartialSolve")
		# If the model is being partially solved and then optimized, fix the error variances
			diag(Smatrix@free[manifestVars,manifestVars]) <- rep(FALSE, length(manifestVars))

		# Reinsert Smatrix in to model
		model[[Sname]] <- Smatrix

		# SO: Returns model with solution to error variances, latent means
		# This model must then be optimized
		return(model)
	}


	# IMPLICIT  solveType@result == "Solve"
	# Full analytical solution is possible

	# Calculate covariance of the latentVars
	SigmaLatent <- NULL

	if (model$data@type == 'raw')
	{
		# Using R's covariance matrix function does not work
		# SigmaLatent <- cov(as.matrix(model$data@observed[, 1:length(latentVars)]))

		# Must make covariance matrix of the manifests "by hand"
		RowxRowT <- apply(as.matrix(model$data@observed[,1:length(latentVars)]), 1, function(row) { as.matrix(row) %*% t(as.matrix(row)) })
		if (!is.matrix(RowxRowT))
			sigma <- sum(RowxRowT)
		else
			sigma <- matrix(apply(as.matrix(RowxRowT), 1, sum), nrow=length(latentVars), ncol=length(latentVars))
		sigma <- sigma/dim(model$data@observed)[[1]]

		# Covariance matrix in SigmaLatent
		SigmaLatent <- sigma - as.matrix(muLatent) %*% t(as.matrix(muLatent))


	}
	else if (model$data@type == 'cov')
		SigmaLatent <- as.matrix(model$data@observed[1:length(latentVars), 1:length(latentVars)])

	# Calculate latent covariance matrix and reinsert in to the S matrix
	# NOTE: Kludgy fix for symmetrization issues
	CLatent <- lambdaInv %*% (SigmaLatent - diag(x=varE, nrow=length(latentVars), ncol=length(latentVars))) %*% t(lambdaInv)
	Smatrix@values[latentVars, latentVars] <- (CLatent + t(CLatent))/2

	# Reinsert error variance in to S matrix
	Smatrix@values[manifestVars, manifestVars] <- diag(x=varE, nrow=length(manifestVars), ncol=length(manifestVars)) # Implicitly: off-diagonal values of this submatrix are zeroed

	# Reinsert Smatrix to model
	model[[Sname]] <- Smatrix

	# Returns fully solved model
	return(model)
}


PPML.Split <- function(model, solveType) {
	# Extract matrices from model
	expectation <- model$expectation

	Aname <- expectation@A
	Sname <- expectation@S
	Fname <- expectation@F
	Mname <- expectation@M
	Amatrix <- model[[Aname]]
	Smatrix <- model[[Sname]]
	Fmatrix <- model[[Fname]]
	Mmatrix <- NULL
	if (!single.na(Mname)) {
		Mmatrix <- model[[Mname]]
	}

	manifestVars <- solveType@manifestVars
	latentVars <- solveType@latentVars

	#Flatten spaces, commas out of model name to prevent problems with expectation functions
	model@name <- gsub(" ", "_", model@name)
	model@name <- gsub(",", "", model@name)

	#leftmodel
	#calculate A realLatents indices for left model
	#first k manifest vars
	selectManifests <- manifestVars[1:length(latentVars)]
	if (solveType@isMatrixSpecified)
		selectData <- model$expectation@dims[selectManifests]
	else
		selectData <- selectManifests

	selectLatents <- latentVars


	leftmodel <- selectSubModelFData(model, selectLatents, selectManifests)
	leftmodel <- mxRename(leftmodel, paste(leftmodel@name, "_leftmodel", sep=""))
	leftmodel@options$UsePPML <- "No"

	# DEVELOPMENT: This will cause problems for models where the left and right model are solved simultaneously
	# Will need to put in a conditional
	# In cases where the left and right model are solved simultaneously,
	# Need to wrap both in a bigModel and put the data in to that bigModel
	# TODO: Move this block to selectsubmodelfdata ?
	if (model$data@type == 'raw') { # RAW DATA
		# Extract model
		leftmodel$data <- model$data
		# Select relevant data
		leftmodel$data@observed <- as.matrix(leftmodel$data@observed[,selectData])
		if (!single.na(leftmodel$data@means))
			leftmodel$data@means <- leftmodel$data@means[selectData]

		# Restore dimnames
		colnames(leftmodel$data@observed) <- selectData
		if (!single.na(leftmodel$data@means))
			colnames(leftmodel$data@means) <- selectData
	}



	#rightmodel
	selectLatents <- latentVars[is.na(pmatch(latentVars, selectLatents))]
	selectManifests <- manifestVars[(length(latentVars)+1):length(manifestVars)]

	if (solveType@isMatrixSpecified)
		selectData <- model$expectation@dims[selectManifests]
	else
		selectData <- selectManifests

	if (model$data@type == 'raw') # RAW DATA
	{
		errData <- sum(model$data@observed[ ,selectData]^2)

		numErrData <- dim(model$data@observed)[[1]] * length(selectData)
	}
	else	# COV DATA
	{
		N <- model$data@numObs
		errData <- sum(diag(as.matrix(model$data@observed))[selectData])

		if (!single.na(model$data@means))
		{
			# Correction factor of N/N-1 at the end is because we're trying
			# to find a sample variance, not a population variance
			# The denominator of the mean is N, not N-1, so this must be corrected
			# to get the sample variance.
			# TRIED: No factor -- diff 9.194952e-2
			# N/(N-1) factor -- diff -8.420196e-2
			# N/(N-1) factor inside ()^2
			# (N-1)/N factor
			# sqrt(N/(N-1)) factor
			# (N+1)/N
			# (N-1)/(N-2)

			errData <- errData + sum(model$data@means[,selectData]^2)
		}

		errData <- errData * N
		numErrData <- N * length(selectData)
	}

	# IN DEVELOPMENT: Rightmodel represented algebraically
	# DEV: Old code, useful for left and right model solved simultaneously?
		# rightmodel <- mxRename(model, paste(model@name,'_rightmodel',sep=""))
		# rightmodel <- selectSubModelFData(rightmodel, selectLatents, selectManifests)
		# rightmodel <- mxOption(rightmodel, "UsePPML", "Split")


	### Create separate 1x1 "error matrix" to be referenced by expectation, constrain to error variance with a label
	# Find error label for Smatrix
	errorLabel <- unique(diag(Smatrix@labels[manifestVars, manifestVars])) # NOTE: Works as long as there's only one error label
	# Create mxMatrix for error parameter
	#  (Value will be first appropriately labeled parameter in the Smatrix, although all should be set the same anyways)
	varE <- mxMatrix( "Full", nrow=1, ncol=1, labels=errorLabel, values=leftmodel$S@values[[ which(leftmodel$S@labels == errorLabel)[[1]] ]], free=TRUE, name="varE" )


	### Create algebra expectation
	sumSqData <- mxMatrix("Full", nrow=1, ncol=1, values = errData, name = "sumSqData")
	numData <- mxMatrix("Full", nrow=1, ncol=1, values = numErrData, name="numData")

	# (Build this algebra:)
	# 		errorLLAlgebra <- mxAlgebra(numData * log(eval(errorLabel)) + sumSqData/eval(errorLabel), name="errorLL")
	# Build algebra string
	expression <- paste("mxAlgebra(numData[1,1] * log(varE[1,1]) + sumSqData[1,1]/varE[1,1], name='errorLL')", sep="")
	# Create algebra
	errorLLAlgebra <- eval(parse(text=expression))


	# DEV: Old code, useful for simultaneously solving left and right models?
		#reunite the two submodel
		# result <-  mxModel(paste('PPMLTransformed_', model@name,sep=""), leftmodel, rightmodel)


	# Build algebra string
	#expression <- paste(paste(model@name,'_leftmodel',sep=""), "objective", sep=".") # "model@name_leftmodel.objective"
	expression <- paste(paste(model@name,'_leftmodel',sep=""), "fitfunction", sep=".") # "model@name_leftmodel.fitfunction"
	expression <- paste(c(expression, "errorLL"), collapse = " + ") # "model@name_leftmodel.objective + errorLL"
	expression <- paste("mxAlgebra(", expression, ", name='TotObj')", sep="") # mxAlgebra(model@name_leftmodel.objective + errorLL, name='TotObj')
	# Create algebra
	objAlgebra <- eval(parse(text=expression))


	#algObjective <- mxAlgebraObjective("TotObj") # Create algebraobjective that call this algebra
	#result <- mxModel(name=model@name, submodels=leftmodel, objective=algObjective, objAlgebra, sumSqData, numData, errorLLAlgebra, varE)
	algFit <- mxFitFunctionAlgebra("TotObj") # Create algebraobjective that call this algebra
	#result <- mxModel(name=model@name, submodels=leftmodel, objective=algObjective, objAlgebra, sumSqData, numData, errorLLAlgebra, varE)


	result <- mxModel(name=model@name, submodels=leftmodel, fitfunction=algFit, objAlgebra, sumSqData, numData, errorLLAlgebra, varE)


#		DEV: Will be necessary for models where left and right model are solved simultaneously,
#		and right model cannot be represented algebraically
#		# Include data in combination model for raw data
#		if (model$data@type == 'raw') {
#			result$data <- model$data
#		}

	result <- mxOption(result, "UsePPML", "Split")

	# DEV: old code -- could be useful for true splitmodel case, where error and
	# leftmodel need to be optimized simultaneously
		# modelnames <- c(paste(model@name,'_leftmodel',sep=""), paste(model@name,'_rightmodel',sep=""))
		# objectives <- paste(modelnames, "objective", sep = ".")
		# objectives <- paste(objectives, collapse = " + ")
		# expression <- paste("mxAlgebra(", objectives, ", name = 'TotObj')", sep = "")
		# algebra <- eval(parse(text=expression))
		# objective <- mxAlgebraObjective("TotObj")
		# result <- mxModel(result, objective, algebra)

	return(result)
}



#@author:	Julian Karch
#		jk3nq@virginia.edu
#@idea>		Timo von Oertzen
#		timo@virginia.edu
selectSubModelFData <- function(model, selectLatents, selectManifests) {
	Aindices <- append(selectManifests, selectLatents)
	if (is.numeric(Aindices))
		Aindices <- sort(Aindices)

	#build the new model
	submodel <- model

	submodel$A <- model$A[Aindices,Aindices]
	submodel$S <- model$S[Aindices,Aindices]
	if (!(is.numeric(selectManifests) && is.numeric(selectLatents))) {
		submodel$F <- model$F[selectManifests, Aindices]
	} else {
		# Matrix-specified:
		# for each Aindice along the columns, find the row that contains a 1
		# keep the columns corresponding to Aindices and those rows
		Frows <- c()
		for (i in 1:dim(model$F)[[2]]) {
			if (any(Aindices == i))
				Frows <- c(Frows, which(as.logical(model$F@values[ ,i])))
		}
		submodel$F <- model$F[Frows, Aindices]
	}

	# Restore dimnames to new matrices
	# Only necessary for path specified models
	if (!(is.numeric(selectManifests) && is.numeric(selectLatents))) {
		submodel@manifestVars <- selectManifests
		submodel@latentVars <- selectLatents
		dimnames(submodel$A)[[1]] <- list(Aindices)[[1]]
		dimnames(submodel$A)[[2]] <- list(Aindices)[[1]]
		dimnames(submodel$S)[[1]] <- list(Aindices)[[1]]
		dimnames(submodel$S)[[2]] <- list(Aindices)[[1]]
		dimnames(submodel$F)[[1]] <- list(selectManifests)[[1]]
		dimnames(submodel$F)[[2]] <- list(Aindices)[[1]]
	}
	else
	{
		submodel$expectation@dims <- submodel$expectation@dims[Aindices]
	}

	if(!is.null(submodel$M)){
		submodel$M <- model$M[1,Aindices]
		# submodel$M@values <- t(submodel$M@values)
		# submodel$M@labels <- t(submodel$M@labels)
		# submodel$M@free <- t(submodel$M@free)
		# submodel$M@lbound <- t(submodel$M@lbound)
		# submodel$M@ubound <- t(submodel$M@ubound)
	}

	if (is.numeric(selectManifests))
		selectData <- model$expectation@dims[selectManifests]
	else
		selectData <- selectManifests

	if(model$data@type == "raw"){
		submodel$data <- NULL
#		submodel$data@observed <- as.matrix(submodel$data@observed[,selectManifests])
	} else if (model$data@type == "cov") {
		# Pull out the proper manifest variables from the covariance matrix
		# to create the appropriate covariance matrix for the submodel
		submodel$data@observed <- as.matrix(submodel$data@observed[selectData,selectData])

		# If means data exists, pull out the appropriate manifest variables
		if (!single.na(submodel$data@means))
			submodel$data@means <- t(as.matrix(submodel$data@means[1,selectData]))

		# Restore dimnames to the new covariance matrix
		colnames(submodel$data@observed) <- selectData
		rownames(submodel$data@observed) <- selectData
		if (!single.na(submodel$data@means))
			colnames(submodel$data@means) <- selectData

	}
	return(submodel)
}


###############################################################################
### TESTING PPML
###############################################################################

PPML.Tool.CheckPPMLDidEqualOrBetter <- function(omxRes, ppmlRes, tolerance)
{
	#diff <- omxRes@output$Minus2LogLikelihood - ppmlRes@output$Minus2LogLikelihood
	diff <- omxRes@output$minimum - ppmlRes@output$minimum

	if (diff < -tolerance)
		#stop("ppmlRes@output$Minus2LogLikelihood not less than or equal to omxRes@output$Minus2LogLikelihood within tolerance of ", tolerance,"\n",omxRes@output$Minus2LogLikelihood," vs ",ppmlRes@output$Minus2LogLikelihood)
		stop("ppmlRes@output$minimum not less than or equal to omxRes@output$minimum within tolerance of ", tolerance,"\n",omxRes@output$minimum," vs ",ppmlRes@output$minimum)


	if (diff > tolerance)
	{
		print(sprintf("PPML found a lower minimum than pure numerical optimization. Diff %g", diff))
		return(TRUE)
	}

	print(sprintf("PPML equal to OpenMx minimum within tolerance. Diff %g", diff))

	return(FALSE)
#		omxCheckCloseEnough(res1@output$Minus2LogLikelihood, res2@output$Minus2LogLikelihood, 0.05)
}


# Functions to test PPML solutions against numerically optimized solutions
# TODO: Move to imxPPMLTester.R (?)
##' imxPPML.Test.Test
##'
##' Test that PPML solutions match non-PPML solutions.
##'
##' @param model the MxModel to evaluate
##' @param checkLL whether to check log likelihood
##' @param checkByName check values using their names
##' @param tolerance closeness tolerance for check
##' @param testEstimates whether to test for the same parameter estimates
##' @details
##' This is an internal function used for comparing PPML and non-PPML solutions.
##' Generally, non-developers will not use this function.
imxPPML.Test.Test <- function(model, checkLL = TRUE, checkByName = FALSE, tolerance=0.5, testEstimates=TRUE) {
	# TODO: Wrap in timing functions for profiling
	model@options$UsePPML = "No"
	res1 <- mxRun(model, suppressWarnings = TRUE) # Standard fit
	model@options$UsePPML = "Yes"
	res2 <- mxRun(model, suppressWarnings = TRUE)	# PPML fit
	# /TODO

	# First model not transformed
	didNotUsePPMLon1 <- is.null(res1@options$UsePPML) || (res1@options$UsePPML == 'No')
	omxCheckTrue( didNotUsePPMLon1 )

	# Second model transformed
	usedPPMLon2 <- (!is.null(res2@options$UsePPML) &&
	 !(res2@options$UsePPML == "Inapplicable" || res2@options$UsePPML == "Yes" || res2@options$UsePPML == "No" ))
	omxCheckTrue( usedPPMLon2 )



	# NOTE: Not checking Hessians, they're never very close -- not sure that
	# a comparison is actually meaningful

	# NOTE: checkLL parameter can turn off log likelihood checking for this test,
	# useful for non-homogeneous error variance cases where the transformed log
	# likelihood is different.
	if (testEstimates)
		PPML.Test.CheckFits(res1, res2, tolerance=tolerance, checkLL = checkLL, checkByName = checkByName, checkHessians = FALSE) # Check standard fit vs PPML model
	else
		PPML.Tool.CheckPPMLDidEqualOrBetter(res1, res2, tolerance)

}

PPML.Test.CheckFits <- function(res1, res2, tolerance, checkHessians = TRUE, checkLL, checkByName) {
	# Check -2logLLs versus each other
	# PPML must be <= OpenMx version, w/in tolerance
	if (checkLL)
	{
		PPMLDidBetter <- PPML.Tool.CheckPPMLDidEqualOrBetter(res1, res2, tolerance)
		if (PPMLDidBetter)
			return() # Estimates, etc are not going to be the same if PPML found a different minimum
	}

	if (checkByName) {

		for (label in labels(res1@output$estimate)) {
			omxCheckCloseEnough(res1@output$estimate[label], res2@output$estimate[label], tolerance)
		}

		oneInTwo <- lapply(labels(res1@output$estimate), function(l) { which(labels(res2@output$estimate) == l) })

		omxCheckCloseEnough(res1@output$standardErrors, as.matrix(res2@output$standardErrors[unlist(oneInTwo)]), tolerance)

	} else {
		# Check parameters versus each other
		# Parameters will be in the same order, so a simple iteration should work
		for ( i in 1:length(res1@output$estimate) ) {
			omxCheckCloseEnough(res1@output$estimate[i], res2@output$estimate[i], tolerance)
		}

		# Check standard errors versus each other
		# Should just be able to check the two vectors directly against each other
#		if (!single.na(res1@output$standardErrors) && !single.na(res2@output$standardErrors))
	#		omxCheckWithinPercentError(res1@output$standardErrors, res2@output$standardErrors, 1) # 1% difference allowed for
	}


	# TODO: Check if @expMeans are replicated...
	#

	# Hessians will be wrong for reversed PPML transforms
	if (checkHessians) {
		# Very loose epsilons for these
		# Check hessianCholeskys versus each other
		omxCheckCloseEnough(res1@output$hessianCholesky, res2@output$hessianCholesky, 0.3)

		# Check calculatedHessians versus each other
		omxCheckCloseEnough(res1@output$calculatedHessian, res2@output$calculatedHessian, 0.3)

		# Estimated Hessians are way, way far off from each other -- doesn't seem worth checking
		## Check estimatedHessians versus each other
		##omxCheckCloseEnough(res1@output$estimatedHessian, res2@output$estimatedHessian, 0.5)
	}
}

##' imxPPML.Test.Battery
##'
##' PPML can be applied to a number of special cases.  This function will test the given model for
##' all of these special cases.
##'
##' Requirements for model passed to this function:
##' - Path-specified
##' - Means vector must be present
##' - Covariance data (with data means vector)
##' - (Recommended) All error variances should be specified on the
##' diagonal of the S matrix, and not as a latent with a loading only
##' on to that manifest
##'
##' Function will test across all permutations of:
##' - Covariance vs Raw data
##' - Means vector present vs Means vector absent
##' - Path versus Matrix specification
##' - All orders of permutations of latents with manifests
##'
##' @param model the model to test
##' @param verbose whether to print diagnostics
##' @param testMissingness try with missingness
##' @param testPermutations try with permutations
##' @param testEstimates examine estimates
##' @param testFakeLatents try with fake latents
##' @param tolerances a vector of tolerances
imxPPML.Test.Battery <- function(model, verbose=FALSE, testMissingness = TRUE, testPermutations=TRUE, testEstimates=TRUE, testFakeLatents=TRUE, tolerances=c(.001, .001, .001))
{
	# Cov data check
	if (!model$data@type == "cov")
		return(NULL)
	# Data means vector presence check
	if (is.null(model$data@means))
		return(NULL)
	# Means vector presence check
	if (is.na(model$expectation@M))
		return(NULL)
	# Path-specified check
	if ( is.null(dimnames(model$S)) || !is.na(model$expectation@dims) )
		return(NULL)

	if (testMissingness)
		nM <- 1
	else
		nM <- 0



	# No missingness, missingness
	for ( missingness in 0:nM)
	{
		if (as.logical(missingness))
			if (verbose) print("Testing with missingness: ")

		# Test with no fake latents
		if (verbose) print("Testing with no fake latents: ")
		testModel <- model
		PPML.Test.Battery.LowLevel(testModel, verbose, missingness=as.logical(missingness), tolerances=tolerances, testPermutations=testPermutations, testEstimates=testEstimates)
		gc()

		# Test with varying numbers of fakeLatents
		if (testFakeLatents) {
			for (i in 1:length(model@manifestVars))
			{
				if (verbose) print(sprintf("Testing with %d fake latents: ", i))
				testModel@options$UsePPML <- "Yes" # Needs to be enabled for fakeLatentFoldout to get solveType
				testModel <- PPML.Tool.fakeLatentFoldout(testModel, i)
				PPML.Test.Battery.LowLevel(testModel, verbose=verbose, missingness=as.logical(missingness), tolerances=tolerances, testPermutations=testPermutations, testEstimates=testEstimates	)
				gc()
			}
		}
	}
}

# This function does bitwise counting up to nMans bits
# Skips 0 == 0,0,...,0 pattern
PPML.Tool.EnumerateMissingnessPatterns <- function(nMans)
{
	patt <- logical(nMans)
	patt[1] <- TRUE

	M <- rbind(patt)
	while(TRUE)
	{
		for (i in 1:nMans)
		{
			if (!patt[i])
			{
				patt[i] <- TRUE

				if (i > 1)
					patt[1:i-1] <- FALSE

				M <- rbind(M, patt)
				break
			}
		}
		if (all(patt))
			break
	}
	return(M)
}


PPML.Test.Battery.LowLevel <- function(model, verbose = FALSE, missingness = FALSE, tolerances, testPermutations, testEstimates) {

	# Function only called by imxPPML.Test.Battery
	# --> Guaranteed to be path-specified

	# Get tolerances
	covMTolerance <- tolerances[1]
	covTolerance <- tolerances[2]
	rawTolerance <- tolerances[3]

	# Determine flags -- NA tolerance -> Don't check
	checkCovM <- !single.na(covMTolerance)
	checkCov <- !single.na(covTolerance)
	checkRaw <- !single.na(rawTolerance)

	# Create raw data
	# Pulled this out: means=as.vector(model$data@means),
	# Don't specify means for raw data
	rawData <- mxData(type="raw", numObs = model$data@numObs, mvrnorm(n=model$data@numObs, mu = model$data@means, model$data@observed) )

	if (missingness)
	{
		# Number of patterns = 2^num_mans - 1  (-1 because there is no data-less pattern)
		numPatts <- 2^length(model@manifestVars) - 1

		# Length of each missingness pattern in the dataset
		pattLen <- floor(model$data@numObs / numPatts)

		if (pattLen == 0)
			return(NULL) # Not enough observations to do all missingness patterns, abort

		patts <- PPML.Tool.EnumerateMissingnessPatterns(length(model@manifestVars))

		for ( i in 0:(dim(patts)[[1]]-1) )
		{
			patt <- as.vector(patts[i+1, ])

			for ( j in 1:length(patt) )
			{
				for ( k in 1:pattLen )
				{
					if (!patt[j])
						rawData@observed[i*pattLen + k, j] <- NA
				}
			}
		}

	}


	#### Path
	if (verbose) print("Testing Path-specified Models:")
	testModel <- model

	### +Expected Means
	## Cov
	if (checkCovM && !missingness)
	{
		if (verbose) print("Testing Covariance Data w/ Expected Means...")
		imxPPML.Test.Test(testModel, tolerance=covMTolerance, testEstimates=testEstimates)
	}

	## Raw
	if (checkRaw)
	{
		if (verbose) print("Testing Raw Data w/ Expected Means...")
		imxPPML.Test.Test(mxModel(testModel, rawData), tolerance=rawTolerance, testEstimates=testEstimates)
	}

	### -Expected Means
	if (checkCov && !missingness)
	{
		testModel[[testModel$expectation@M]] <- NULL
		testModel$expectation@M <- as.character(NA)
		## Cov
		if (verbose) print("Testing Covariance Data w/o Expected Means...")
		imxPPML.Test.Test(mxModel(testModel, mxData(type=testModel$data@type, numObs=testModel$data@numObs, observed=testModel$data@observed)), tolerance=covTolerance, testEstimates=testEstimates)
		## Raw: Can't test for Raw -Expected Means, as raw data requires an expected means vector
	}


	#### Matrix
	if (verbose) print("Testing Matrix-specified Models:")

	matrixModel <- PPML.Tool.PathToMatrix(model)
	# Try each unique permutation

	# -First permutation in the array is just the unpermuted case
	# -OpenMx doesn't like having its matrices permuted, so just get the unPPMLed
	# output from the unpermuted case, and permute it accordingly to check
	# against PPMLed results

	testModel <- matrixModel
	if (verbose) print("Testing unpermuted model...")
	### +Means
	## Cov
	if (checkCovM && !missingness)
	{
		if (verbose) print("Testing Covariance Data w/ Expected Means...")
		testModel@options$UsePPML <- "No"
		vanillaCovMRes <- mxRun(testModel)
		testModel@options$UsePPML <- "Yes"
		PPMLCovMRes <- mxRun(testModel)

		usedPPML <- (!is.null(PPMLCovMRes@options$UsePPML) &&
		 !(PPMLCovMRes@options$UsePPML == "Inapplicable" || PPMLCovMRes@options$UsePPML == "Yes" || PPMLCovMRes@options$UsePPML == "No" ))
		omxCheckTrue( usedPPML )
		didBetter <- PPML.Tool.CheckPPMLDidEqualOrBetter(vanillaCovMRes, PPMLCovMRes, tolerance=covMTolerance)
		if (!didBetter && testEstimates)
			omxCheckCloseEnough(vanillaCovMRes@output$estimate, PPMLCovMRes@output$estimate, covMTolerance)
	}


	## Raw
	if (checkRaw)
	{
		if (verbose) print("Testing Raw Data w/ Expected Means...")
		testModel@options$UsePPML <- "No"
		vanillaRawRes <- mxRun(mxModel(testModel, rawData) )


		testModel@options$UsePPML <- "Yes"
		PPMLRawRes <- mxRun(mxModel(testModel, rawData))
		usedPPML <- (!is.null(PPMLRawRes@options$UsePPML) &&
		 !(PPMLRawRes@options$UsePPML == "Inapplicable" || PPMLRawRes@options$UsePPML == "Yes" || PPMLRawRes@options$UsePPML == "No" ))
		omxCheckTrue( usedPPML )
		didBetter <- PPML.Tool.CheckPPMLDidEqualOrBetter(vanillaRawRes, PPMLRawRes, tolerance=rawTolerance)
		if (!didBetter && testEstimates)
			omxCheckCloseEnough(vanillaRawRes@output$estimate, PPMLRawRes@output$estimate, rawTolerance)
	}

	### -Means
	if (checkCov && !missingness)
	{
		testModel[[testModel$expectation@M]] <- NULL
		testModel$expectation@M <- as.character(NA)
		dataNoMeans <- mxData(type=testModel$data@type, numObs=testModel$data@numObs, observed=testModel$data@observed)
		## Cov
		if (verbose) print("Testing Covariance Data w/o Expected Means...")
		testModel@options$UsePPML <- "No"
		vanillaCovRes <- mxRun(mxModel(testModel, data=dataNoMeans))

		testModel@options$UsePPML <- "Yes"
		PPMLCovRes <- mxRun(mxModel(testModel, data=dataNoMeans))
		usedPPML <- (!is.null(PPMLCovRes@options$UsePPML) &&
		 !(PPMLCovRes@options$UsePPML == "Inapplicable" || PPMLCovRes@options$UsePPML == "Yes" || PPMLCovRes@options$UsePPML == "No" ))
		omxCheckTrue( usedPPML )
		didBetter <- PPML.Tool.CheckPPMLDidEqualOrBetter(vanillaCovRes, PPMLCovRes, tolerance=covTolerance)
		if (!didBetter && testEstimates)
			omxCheckCloseEnough(vanillaCovRes@output$estimate, PPMLCovRes@output$estimate, covTolerance)
	}


	if (!testPermutations)
		return();

	# Want to check all relevant permutations of manifests and latents; however,
	# switching the order of any two latents or any two manifests should not pose any
	# kind of problem.  So, do only unique permutations treating all manifests as
	# the same and all latents as the same for the purposes of the permutation.
	defPerm <- 1:dim(model[[model$expectation@F]])[[2]]
	manOrLat <- apply(model[[model$expectation@F]]@values, 2, sum)
	manIndices <- which(manOrLat == 1)
	latIndices <- which(manOrLat == 0)
	# Get unique permutations
	perms <- unique(PPML.Tool.EnumeratePermutations(manOrLat))
	# Insert actual indices for mans or lats in to permutations
	perms <- t(apply(perms, 1, function(row) { row[which(row==1)] <- manIndices; row[which(row==0)] <- latIndices; return(row) } ))


	# PERMUTATIONS OF MATRIX MODELS
	# Get a standard ordering for estimates
#	estNamesM <- names(vanillaCovMRes@output$estimate)
#	estNames <- names(vanillaCovRes@output$estimate)

	# Minima tracking
	covMEsts <- list()
	covEsts <- list()
	rawEsts <- list()

	for (i in 2:dim(perms)[[1]]) {

		if (verbose)
		{
			print("Testing permutation: ")
			print(as.vector(perms[i,]))
		}
		testModel <- matrixModel
		# PERMUTING THE MODEL
		# Build permutation matrix
		#P <- matrix(nrow=length(defPerm), ncol=length(defPerm),
		#	unlist(lapply(perms[i,], function(item){ as.numeric(defPerm == item) } )) )

		# Permute matrices
		# A %*% P
		testModel[[testModel$expectation@A]] <- testModel[[testModel$expectation@A]][ , perms[i, ] ]
		# t(P) %*% (A %*% P)
		testModel[[testModel$expectation@A]] <- testModel[[testModel$expectation@A]][ perms[i, ], ]

		# For S -- Special considerations necessary, as the operating on S in this manner breaks
		# the symmetry of the matrix and changes the output
		#   So, instead of directly: Extract labels, free, values, operate on them, reinsert
		Sfree <- testModel[[testModel$expectation@S]]@free
		Slabels <- testModel[[testModel$expectation@S]]@labels
		Svalues <- testModel[[testModel$expectation@S]]@values
		# S %*% P
		Sfree <- Sfree[ , perms[i, ] ]
		Slabels <- Slabels[ , perms[i, ] ]
		Svalues <- Svalues[ , perms[i, ] ]
		# t(P) %*% (S %*% P)
		Sfree <- Sfree[ perms[i, ], ]
		Slabels <- Slabels[ perms[i, ], ]
		Svalues <- Svalues[ perms[i, ], ]
		# Reinsert
		testModel[[testModel$expectation@S]]@free <- Sfree
		testModel[[testModel$expectation@S]]@labels <- Slabels
		testModel[[testModel$expectation@S]]@values <- Svalues

		# F %*% P
		testModel[[testModel$expectation@F]] <- testModel[[testModel$expectation@F]][ , perms[i, ] ]

		# M %*% P
		testModel[[testModel$expectation@M]] <- testModel[[testModel$expectation@M]][ , perms[i, ] ]


		# Don't need to permute data because manifest variables are always
		# in the same order.
		# Permute data cov matrix, means vector
#		dcov <- testModel$data@observed
#		dcov <- dcov[ , intersect(perms[i, ], manIndices) ]
#		dcov <- dcov[ intersect(perms[i, ], manIndices) , ]
#		testModel$data@observed <- dcov
#		dmeans <- t(as.matrix(testModel$data@means[ intersect(perms[i, ], manIndices) ]))
#		dimnames(dmeans)[[2]] <- (dimnames(testModel$data@means)[[2]])[ intersect(perms[i,], manIndices) ]
#		testModel$data@means <- dmeans


		# Permute raw data
#		rawDataPerm <- rawData
#		rawDataPerm@observed <- rawDataPerm@observed[ , intersect(perms[i, ], manIndices) ]

		# Permute expectation dims
		testModel$expectation@dims <- testModel$expectation@dims[ perms[i, ] ]

		# Enable PPML for testing permuted model
		testModel@options$UsePPML <- "Yes"

		# NOTE: Estimates are not checked for the permutated models
		# Permuting the matrices in this way has a way of finding alternative, equally good minimums
		# Should probably investigate this tendency further

		### +Means
		## Cov
		if (checkCovM && !missingness)
		{
			if (verbose) print("Testing Covariance Data w/ Expected Means...")
			PPMLCovMRes <- mxRun(testModel)
			usedPPML <- (!is.null(PPMLCovMRes@options$UsePPML) &&
			 !(PPMLCovMRes@options$UsePPML == "Inapplicable" || PPMLCovMRes@options$UsePPML == "Yes" || PPMLCovMRes@options$UsePPML == "No" ))
			omxCheckTrue( usedPPML )
			didBetter <- PPML.Tool.CheckPPMLDidEqualOrBetter(vanillaCovMRes, PPMLCovMRes, covMTolerance)
			#omxCheckCloseEnough(vanillaCovMRes@output$estimate, PPMLCovMRes@output$estimate, .05)
#			covMEsts <- append(covMEsts, list(PPMLCovMRes@output$estimate[estNamesM]))
		}

		## Raw
		if (checkRaw)
		{
			if (verbose) print("Testing Raw Data w/ Expected Means...")
			PPMLRawRes <- mxRun(mxModel(testModel, rawData))
			usedPPML <- (!is.null(PPMLRawRes@options$UsePPML) &&
			 !(PPMLRawRes@options$UsePPML == "Inapplicable" || PPMLRawRes@options$UsePPML == "Yes" || PPMLRawRes@options$UsePPML == "No" ))
			omxCheckTrue( usedPPML )
			didBetter <- PPML.Tool.CheckPPMLDidEqualOrBetter(vanillaRawRes, PPMLRawRes, rawTolerance)
			#omxCheckCloseEnough(vanillaRawRes@output$estimate, PPMLRawRes@output$estimate, .05)
			#rawEsts <- append(rawEsts, list(PPMLRawRes@output$estimate[estNamesM]))
		}

		### -Means
		if (checkCov && !missingness)
		{
			testModel[[testModel$expectation@M]] <- NULL
			testModel$expectation@M <- as.character(NA)
			## Cov
			if (verbose) print("Testing Covariance Data w/o Expected Means...")
			PPMLCovRes <- mxRun(mxModel(testModel, data=dataNoMeans))
			usedPPML <- (!is.null(PPMLCovRes@options$UsePPML) &&
			 !(PPMLCovRes@options$UsePPML == "Inapplicable" || PPMLCovRes@options$UsePPML == "Yes" || PPMLCovRes@options$UsePPML == "No" ))
			didBetter <- PPML.Tool.CheckPPMLDidEqualOrBetter(vanillaCovRes, PPMLCovRes, covTolerance)
#			covEsts <- append(covEsts, list(PPMLCovRes@output$estimate[estNames]))
			#omxCheckCloseEnough(vanillaCovRes@output$estimate, PPMLCovRes@output$estimate, .05)
		}
	}
}

PPML.Tool.EnumeratePermutations <- function(elements) {
	if (length(elements) == 1)
		return(elements)
	pMat <- matrix(nrow=factorial(length(elements)), ncol=length(elements))
	blockSize <- factorial(length(elements) - 1)
	for (i in 0:(length(elements)-1) ) {
		pMat[ (i*blockSize+1) : ((i+1)*blockSize), 1] <- elements[i+1]
		pMat[ (i*blockSize+1) : ((i+1)*blockSize), 2:length(elements)] <- as.matrix(PPML.Tool.EnumeratePermutations(elements[-(i+1)]))
	}
	return(pMat)
}

# Function to respecify a path-specified model as a matrix-specified model
PPML.Tool.PathToMatrix <- function(model) {
	### Extract expectation
	expectation <- model$expectation
	if(is.null(expectation) || !is(expectation, "MxExpectationRAM")) {
		return(NA)
	}

	### Safely extract ASF(M) matrices by using names from the objective
	Aname <- expectation@A
	Sname <- expectation@S
	Fname <- expectation@F
	Mname <- expectation@M

	Amatrix <- model[[Aname]]
	Smatrix <- model[[Sname]]
	Fmatrix <- model[[Fname]]

	# Extract variable names for new expectation
	varNames <- dimnames(Fmatrix)[[2]]

	### Remove dimnames from matrices
	dimnames(Amatrix) <- NULL
	dimnames(Smatrix) <- NULL
	dimnames(Fmatrix) <- NULL

	# Special: Matrix of expected means
	Mmatrix <- NULL
	if (!single.na(Mname)) {
		Mmatrix <- model[[Mname]]
		dimnames(Mmatrix) <- NULL
	}

	# Create new expectation
	newexpectation <- mxExpectationRAM(A=Aname,S=Sname,F=Fname,M=Mname, dimnames=varNames)
	# Remove manifestvars, latentvars
	model@latentVars <- character(0)
	model@manifestVars <- character(0)

	# Return matrix-specified model
	return( mxModel(name = model@name, Amatrix, Smatrix, Fmatrix, Mmatrix, newexpectation, model$fitfunction, model$data, model@constraints ) )
}

# This function only accepts path-specified models
# Takes the manifest variable at manIndex, and folds its variance out in to a fake latent
# Fake latent := Manifest variance specified by a latent variable with only one loading
#  of value 1, to the manifest in question ==> Variance of the manifest is specified
#  in the fakelatent, not in the manifest variable itself
PPML.Tool.fakeLatentFoldout <- function(model, manIndex)
{
	# Path-specified check
	if ( is.null(dimnames(model$S)) || !single.na(model$expectation@dims) )
		return(NULL)

	Sname <- model$expectation@S
	Smatrix <- model[[Sname]]


	# Idiot check: Make sure manifest[manIndex] exists
	if ( manIndex > length(model@manifestVars) )
		return(NULL)

	man <- model@manifestVars[manIndex]

	# Save info
	FLvar <- Smatrix@values[man,man]
	FLlabel <- Smatrix@labels[man,man]
	FLfree <- Smatrix@free[man,man]

	# Remove normally defined variance from Smatrix
	Smatrix@values[man,man] <- 0
	Smatrix@free[man,man] <- FALSE
	Smatrix@labels[man,man] <- NA

	# Reinsert stripped Smatrix
	model[[Sname]] <- Smatrix

	## Add fakeLatent
	# Generate name -- man is the name of the variable in path-specified models
	FLname <- sprintf("%s_FL", man)

	# Create path from fakelatent to manifest variable
	oneHeadedPath <- mxPath(from=FLname, to=man, arrows=1, free=FALSE, values=1)

	# Create two-headed arrow on fake latent
	twoHeadedPath <- mxPath(from=FLname, arrows=2, free=FLfree, values = FLvar, labels = FLlabel)

	# Recreate model
	model <- mxModel(model, latentVars=FLname, oneHeadedPath, twoHeadedPath)
	return(model)
}
OpenMx/OpenMx documentation built on April 17, 2024, 3:32 p.m.