using mxModel

18 replies [Last post]
tbates's picture
Offline
Joined: 07/31/2009
tbates's picture
Offline
Joined: 07/31/2009
?MxModel tells us that remove

?MxModel tells us that remove is:

If TRUE, elements listed in this statement are added to the original model. If FALSE, elements listed in this statement are removed from the original model.

That seems back to front. Surely if remove is true, statements are removed?

mspiegel's picture
Offline
Joined: 07/31/2009
Fixed.

Fixed.

mspiegel's picture
Offline
Joined: 07/31/2009
# Places path in supermodel

# Places path in supermodel and all submodels,
# if-and-only-if a model is RAM type and contains
# manifest or latent variables for the 'from' and 'to' fields
# of the given path.
pathClobberRecursive <- function(model, path) {
	if (is(model, "MxRAMModel")) {
		from <- path$from
		to <- path$to
		vars <- union(model@manifestVars, model@latentVars)
		if (all(from %in% vars) && all(to %in% vars)) {
			model <- mxModel(model, path)
		}
	}
	model@submodels <- lapply(model@submodels, pathClobberRecursive, path)
	return(model)
}

I would not make this a part of the OpenMx API. It would be akin to handing the users a loaded shotgun and saying "don't hurt yourself".

tbates's picture
Offline
Joined: 07/31/2009
I like guns :-) Given that

I like guns :-)

Given that the demo writer clearly thought that was what would happen, and I did too, maybe give us that gun, but put a safety catch on it: with clobber=FALSE the default.

PS: You really know both your R and recursive programming to punch out that function so quickly! I didn't see how "model<-mxModel(m, p)" was doing anything other than restating the problem until glancing down to the call to self... Nice work.

PPS: Its this kind of thing that was impossible in oldMx: I think a library of helper function like this will accumulate quickly!

PPPS: Does leaving stuff like paths lying around in supermodels have any nasty side-effects?

tbates's picture
Offline
Joined: 07/31/2009
Bug in overwriting path with

Bug in overwriting path with mxPath() in model?

I wrote a modified script for univariate ACE to AE... but the fit didn't change when dropping C.

The relevant code-chunk is this, trying to build an AE model derived from a base ACE model

twinAEModel <- mxModel( twinACEModel, name="twinAEModel",
   manifestVars=selVars,
  latentVars=aceVars,
  # This next line doesn't work if used
	 # mxPath(from=c("C1","C1"), to=c("bmi1","bmi2"), arrows=1, free=FALSE, values=0, label="cfixed")
  # I thought that meant all paths in a single call have to found in one submodel... but...
  # This also doesn't work
   # mxPath(from="C1", to="bmi1", arrows=1, free=FALSE, values=0, label="cfixed"),
   # mxPath(from="C2", to="bmi2", arrows=1, free=FALSE, values=0, label="cfixed")
 
# It seems that ALL of the paths need to be rebuild, even the ones that are not being touched....
# this works
   mxPath(from=c("A1","C1","E1"), to="bmi1", arrows=1, free=c(T,F,T), values=c(.6,0,.6), label=c("a","cfixed","e")),
   mxPath(from=c("A2","C2","E2"), to="bmi2", arrows=1, free=c(T,F,T), values=c(.6,0,.6), label=c("a","cfixed","e"))
)
 
Is that a bug?

mspiegel's picture
Offline
Joined: 07/31/2009
In the demo

In the demo "UnivariateTwinAnalysis_PathRaw", twinACEModel is a model that contains two nested submodels. So in fact the following statement from the demo is incorrect:

twinAEModel <- mxModel(twinACEModel, type="RAM",
    manifestVars=selVars, latentVars=aceVars,
    mxPath(from=c("A1","C1","E1"), to="bmi1", ...),
    mxPath(from=c("A2","C2","E2"), to="bmi2", ...))

What SHOULD be happening is the paths should be removed from each submodel.

mspiegel's picture
Offline
Joined: 07/31/2009
Wait twinAEModel is a RAM

Wait twinAEModel is a RAM model??? This implies that an automatic RAM objective function is created, over-writing the algebra objective from twinACEModel. OK, I don't know what is the intent of the demo.

mspiegel's picture
Offline
Joined: 07/31/2009
I think I've figured out the

I think I've figured out the intent of the demo. I'm checking in a fix in a few minutes. It would be helpful to have some omxCheckCloseEnough() statements at the end of the script to catch these types of errors.

tbates's picture
Offline
Joined: 07/31/2009
hi Mike, I see why the demo

hi Mike,

I see why the demo writer called the c path "cfixed", instead of leaving it called "c" - which is what it is... trying this instead gives an error

mxPath(from=c("A1","C1","E1"), to="bmi1", arrows=1, free=c(T,F,T), values=c(.6,0,.6), label=c("a","c","e")),
 
# Running twinACE  (should be AE - they forget to change the internal name...)
# Error: The label 'c' has been assigned to a free parameter and a fixed value!

So the bug is that when a path is updated, the question of whether to let the new output of mxPath squash the old path when that involves side effects on other paths (like fixing them) has not been resolved (at least as of build 725)

Given previous discussion here that mxPath() overwrites existing paths, clearly setting the path to fixed should just go ahead and set paths of the same name in other groups in the container to fixed also.

mspiegel's picture
Offline
Joined: 07/31/2009
The bug was fixed in revision

The bug was fixed in revision 723. Make sure your local repository is identical to the subversion repository by running "svn status". That should report no differences. If you see something like "M UnivariateTwinAnalysis_PathRaw.R" then you need to delete your local copy and then run "svn update" again. In the corrected demo, there is no free parameter named 'cfixed' because the free parameter 'c' from the ACE model is replaced in the AE model with a fixed values named 'c'. In the broken demo, the path "cfixed" was placed in the super-model and the 'c' free parameters in the submodels were not deleted. Hence the error about assigning 'c' to a free parameter and a fixed value.

tbates's picture
Offline
Joined: 07/31/2009
OK, I have that now - i must

OK, I have that now - i must have touched the old file and stopped it updating.

Is there a way to talk to the supermodel (hereafter known as "Elle"), and get it to direct mxPath requests to all sub-models?

Usecase: Often something done to one submodel would need to doing to all submodels, and in a complex design there might be 7 of these, so it would be handy to be able to say "do this everywhere"

tbates's picture
Offline
Joined: 07/31/2009
Script error and code bug?

Script error and code bug?

DROP C not working in trunk/demo/UnivariateTwinAnalysis_PathRaw.R

Hi there,
I trying to build a univariate twin raw data script, and this prompts some questions.

In the original script the fit changes, but the values for C don't drop to 0 for the AE model.

in the ACE model, the three paths in each group are labelled label=c("a","c","e"), and then accessed here to compute A C and E:

A <- mxEval(a*a, twinAEFit)
C <- mxEval(c*c, twinAEFit)
E <- mxEval(e*e, twinAEFit)

Then in the AE model the labels are set to c("a","cfixed","e"))

Then there is a critical error in the script: the exact same a,c,e labels as before are used again to calculate A C and E for the AE model.

In addition to the error in the script code, isn't there also a bug?

BUG?: given that the C path has been overwritten by this:

    mxPath(from=c("A1","C1","E1"), to="bmi1", arrows=1, free=c(T,F,T), values=c(.6,0,.6), label=c("a","cfixed","e")),
    mxPath(from=c("A2","C2","E2"), to="bmi2", arrows=1, free=c(T,F,T), values=c(.6,0,.6), label=c("a","cfixed","e"))

Why doesn't this return an error: no path with that label?
C <- mxEval(c*c, twinAEFit)
given it should be (which does return C = exactly 0)
C <- mxEval(cfixed* cfixed, twinAEFit)

tbates's picture
Offline
Joined: 07/31/2009
when you make a model

when you make a model containing models, I see that the new container does not know about the latentVars and manifestVars of its nestlings.

Questions:

1. Can nestlings have different latentVars and manifestVars from each other?
2. What is the purpose of latentVars and manifestVars for containers?
2a: If it is too allow communication with the nestlings, then I think the super-model's latentVars and manifestVars should automatically updated to be the intersection of the nested models latentVars and manifestVars?

That way you can't get things out of whack.

Just for context, this arose for me in changing from this

twinAEModel <- mxModel(twinACEModel, type="RAM",
    manifestVars=selVars,
    latentVars=aceVars,
    mxPath(from=c("A1","C1","E1"), to="bmi1", arrows=1, free=c(T,F,T), values=c(.6,0,.6), label=c("a","cfixed","e")),
    mxPath(from=c("A2","C2","E2"), to="bmi2", arrows=1, free=c(T,F,T), values=c(.6,0,.6), label=c("a","cfixed","e"))
    )

to this

twinAEModel <- mxModel( twinACEModel, 
   mxPath(from=c("C1","C2"), to=c("bmi1","bmi2"), arrows=1, free=FALSE, values=0, label="cfixed") 
  )

got
Error: The following are neither manifest nor latent variables: 'C1', 'C2'

Which then ran fine after I added manifestVars and latentVars to twinACEModel

FYI, the relevant preceding code is:

MZ <- mxModel("MZ", type="RAM",
    manifestVars=selVars,
    latentVars  =aceVars,
    mxPath(from=aceVars, arrows=2, free=FALSE, values=1),
    mxPath(from="one", to=aceVars, arrows=1, free=FALSE, values=0),
    mxPath(from="one", to=selVars, arrows=1, free=TRUE, values=20, labels= c("mean","mean")),
    mxPath(from=c("A1","C1","E1"), to="bmi1", arrows=1, free=TRUE, values=.6, label=c("a","c","e")),
    mxPath(from=c("A2","C2","E2"), to="bmi2", arrows=1, free=TRUE, values=.6, label=c("a","c","e")),
    mxPath(from="C1", to="C2", arrows=2, free=FALSE, values=1),
    mxPath(from="A1", to="A2", arrows=2, free=FALSE, values=1), # MZ correlation for T1 and T2
    mxData(mzfData, type="raw")
)
 
# Build DZ group based on MZ just change the data and A-coefficient
DZ = mxModel(MZ, name="DZ",
    mxPath(from="A1", to="A2", arrows=2, free=FALSE, values=.5), # overwrite: value = .5 for DZ
    mxData(dzfData, type="raw")
)
 
# Make a super-model to contain both the MZ and DZ groups
twinACEModel <- mxModel("twinACE", type="RAM", # have to call this RAM, so it will accept paths later on 
    # oops, also need to add latent and manifest here, but have not yet
    MZ, DZ,
    mxAlgebra(MZ.objective + DZ.objective, name="twin"), 
    mxAlgebraObjective("twin")  # the algebra we are trying to minimise
)

tbates's picture
Offline
Joined: 07/31/2009
help for ?mxModel notes that

help for ?mxModel notes that if you provide a model as the first parameter of mxModel(), then "all elements of that model are placed in the resulting MxModel object"

Given that, I assume that given an input "share" which has type=RAM

mxModel(share,
mxPath(from="A1", to="A2", arrows=2, free=FALSE, values=1),
mxData(mzfData, type="raw"),
type="RAM", name="MZ")

It is redundant to re-specify the type now, yes? Seems to work leaving it out.

And just to followup, if you create a path than already exists in the base model, it is overwritten?

Tried this and it seemed to work (specifying all paths in mz, then making dz simply over-write the A1<->A2 path as FIXED, 0.5)

#Fit ACE Model with RawData and Path-Style Input
mz <- mxModel("MZ", type="RAM",
    manifestVars=selVars,
    latentVars=aceVars,
    mxPath(from=aceVars, arrows=2, free=FALSE, values=1),
    mxPath(from="one", to=aceVars, arrows=1, free=FALSE, values=0),
    mxPath(from="one", to=selVars, arrows=1, free=TRUE, values=20, labels= c("mean","mean")),
    mxPath(from=c("A1","C1","E1"), to="bmi1", arrows=1, free=TRUE, values=.6, label=c("a","c","e")),
    mxPath(from=c("A2","C2","E2"), to="bmi2", arrows=1, free=TRUE, values=.6, label=c("a","c","e")),
    mxPath(from="C1", to="C2", arrows=2, free=FALSE, values=1),
    mxPath(from="A1", to="A2", arrows=2, free=FALSE, values=1),
    mxData(mzfData, type="raw") )
 
twinACEModel <- mxModel("twinACE", 
    mz,
    mxModel(mz, name="DZ",
        mxPath(from="A1", to="A2", arrows=2, free=FALSE, values=.5),
        mxData(dzfData, type="raw") 
    ),
    mxAlgebra(MZ.objective + DZ.objective, name="twin"), 
    mxAlgebraObjective("twin")
)

Steve's picture
Offline
Joined: 07/30/2009
type="x" is there so that

type="x" is there so that other path-able model formulations can be used. For instance, Paras is looking at implementing type="LISREL". Until there is a second model type, type= is redundant. Don't rely on it remaining so.

tbates's picture
Offline
Joined: 07/31/2009
I think the context here was

I think the context here was not not "redundant as in doesn't get used" but rather "redundant" in that it will be imported from the model passed in as parameter 1, so setting it again is ... overkill"

Is that correct? I think already you can't leave out type="RAM", and expect to set paths... I bumped into this with a supermodel (container) that had no type set, and when I tried to drop paths it said "model type does not take paths"

Steve's picture
Offline
Joined: 07/30/2009
I see. You are correct. It

I see. You are correct. It doesn't hurt to set type="RAM" twice, but is unnecessary. However, we do insist that you set type="RAM" if you plan to use mxPath. One might consider using mxPath as implying type="RAM", and while that could work for the moment, it will break in the long run, so we have insisted on people being explicit about type="RAM" now so that their code won't break when type="LISREL" is implemented.

mspiegel's picture
Offline
Joined: 07/31/2009
Yes, it is redundant to

Yes, it is redundant to re-specify the type. When the first argument to mxModel is a model, then a copy of that model is used as the starting point for the model that will be returned from the function (pass by value). Also, if a path is specified with the same "from" and "to" fields as a path that already exists, it will overwrite the existing path.