You are here

mxEval(formula, model, environment = model) # add environment

mxEval looks in the parent environments to find objects. This can be handy, allowing reference to such objects in algebra. However, when used in the context of getting things out of models (quite a common use-case), it can be dangerous: mxEval might return an object with the same name from the environment, rather than from inside the model you have set as the target.

Example. Say you have a matrix

[1,]    1

If you also have a variable nVar in my environment, set to whatever it happens to be set to, say "2", then mxEval() doesn't return the nVar matrix from the model, but rather nVar from the environment:

nVar = 2
mxEval(nVar, m1)

(parenthetically, I had thought it searched exhaustively inside the model before traversing the parent environment, but this is not the case.)

[1] 2
[1,]    1

i.e., it has discovered nVar in the environment, even though it exists in the submodel, and there’s no warning.

Did that used to happen?

I suggest we add an option to mxEval(), something like “env = 'self’ ” for when we’d rather get an “object not found” error than risk mistakenly accessing a variable from other environments. It's really pretty hairy...

then this would occur:

mxEval(nVar, m1, env=’self’)
# error: var not found
mxEval(nVar, m1$top, env=’self’)
[1,]    1
mxEval(nVar, m1, env=global)

Tue, 08/19/2014 - 17:31
Thu, 09/29/2016 - 12:58


I'm fine with this. If you feel strongly about it, then implement it.

good idea, too hard

I tried a couple of things involving the following line in MxEval.R

env <- parent.frame()

but had no luck in getting things to work. Under one scenario (env <- new.env(parent = emptyenv())), I was able to get mxEval to not find something in the parent environment. However, it also couldn't find things like "[" and "%*%", so that's not good.

There are three more things that deserve note. First, mxEval does not search within submodels. So the example given in the original post should not work, and the proposed change would not alter that. Second, mxEval works a lot like eval, in fact calling eval and the related deparse at several points. These R functions search their immediate environment, then their parent's environment, and so on. Thus, mxEval works like the rest of R: frustrating at times but generally sensible. Third, it's generally good programming practice to use different names for the OpenMx Objects and the R Objects, which avoids this issue.

The good thing about checking exists() for each element of the algebra is that asking about a var in a sub model would be flagged as non-existent, and the error could say, "Perhaps you made a typo, or perhaps the parameter is in a sub-model. If so call it as "modelName.varName"