nonlinear constraints
Posted on

I was wondering if nonlinear constraints were available and if so, if additional parameters can be incorporated into the nonlinear constraint. An example involves nonlinear growth curves (Browne, 1993).
Hey Kevin, They can, using
Hey Kevin,
They can, using the mxConstraint function, but that function only works on mxAlgebra and mxMatrix objects.
If you want to constrain individual parameters, you'll have to put those parameters in additional matrices. For instance, consider a RAM model with matrices A, S and F (which you could create using either paths or matrices, and using whatever objective function you like). You have two parameters "lambda1" and "lambda2" in your A matrix, and you want "lambda1" to be greater than "lambda2".
You'll have to create two new matrices, each a 1x1 and containing one free parameter (let's call them "L1" and "L2"). Label those free parameters "lambda1" and "lambda2". Then use mxConstraint to define the relationship between "L1" and "L2". As long as you include "L1", "L2" and your MxConstraint object in your model, you'll constrain those parameters. Here's a quick mock-up of an example.
model <- mxModel("testing nonlinear constraints",
mxData(whatever....),
mxMatrix(whatever....name="A"), #has parameters lambda1 and lambda2 in labels argument
mxMatrix(whatever....name="S"),
mxMatrix(whatever....name="F"),
mxMatrix(whatever....name="M"),
mxMatrix(Full, nrow=1, ncol=1, free=T, values=1, label="lambda1", name="L1"),
mxMatrix(Full, nrow=1, ncol=1, free=T, values=1, label="lambda2", name="L2"),
mxConstraint("L1", ">", "L2"),
mxRAMObjective("A","S","F","M")
)
Log in or register to post comments
In reply to Hey Kevin, They can, using by Ryne
Thanks Ryne. In the
Thanks Ryne. In the mxConstraint command, is it possible to write out "complex" nonlinear functions? A simple case would be an exponential curve (loading1 = 1 - exp(-L1 * 5)).
Thanks again,
k
Log in or register to post comments
In reply to Thanks Ryne. In the by kgrimm
The constraint (loading1 = 1
The constraint (loading1 = 1 - exp(-L1 * 5)) can be expressed. The left-hand side and right-hand side of the mxConstraint() function are both mxAlgebra() expressions. As a consequence of this fact, both the left-hand side and right-hand side must evaluate to matrices. In order to express scalar constraints, use algebras that evaluate to 1x1 matrices. The exp() function is supported by the mxAlgebra() function. To see a full list of the matrix operators and functions available, type ?mxAlgebra in R.
Log in or register to post comments
In reply to The constraint (loading1 = 1 by mspiegel
Thanks. I'm still not sure if
Thanks. I'm still not sure if I understand some of the intricacies of what you're describing. So, if I want to force a factor loading, say in the "A" matrix in RAM notation, to be equal to "1-exp(L1*5)" where L1 is a parameter to be estimated. Would it be necessary to create a 1x1 matrix with an element labeled the same as the factor loading in the "A" matrix I'm trying to constrain and refer to this 1x1 matrix in mxConstraint? Thanks again.
Log in or register to post comments
In reply to Thanks. I'm still not sure if by kgrimm
It sort of looks like that to
It sort of looks like that to me, but Michael S will know for sure. This makes me wonder whether we need a
as.mxMatrix()
function, which could be applied to mxPaths or mxMatrix elements which were identified by label="" arguments. Thus the explicit creation of matrices specifically for the purposes of constraints would not be necessary, simplifying Michael's mock-up example to:
model <- mxModel("testing nonlinear constraints",
mxData(whatever....),
mxMatrix(whatever....name="A"), #has parameters lambda1 and lambda2 in labels argument
mxMatrix(whatever....name="S"),
mxMatrix(whatever....name="F"),
mxMatrix(whatever....name="M"),
mxConstraint(as.mxMatrix(lambda1), ">", as.mxMatrix(lambda2)),
mxRAMObjective("A","S","F","M")
)
Log in or register to post comments
In reply to It sort of looks like that to by neale
Unfortunately, "lambda1"
Unfortunately, "lambda1" isn't a named entity. There can be multiple "lambda1"s in the model (to enforce equality constraints, for instance), which can't occur with named entities. Using as.mxMatrix("lambda1") would look for an MxModel, MxAlgebra or other object, not an element in an MxMatrix object. The closest thing we have is mxMatrix itself.
Kevin, it is possible to specify an entire set of constraints in a single mxConstraint object. If all of your loadings get the same transformation, they all go in the same matrix.
mxMatrix(whatever...name="A") #has parameters lambda1-4
....
mxMatrix("Full", 1, 4, TRUE, values=c(1,2,3,4), labels=c("lambda1", "lambda2", "lambda3", "lambda4"), name="alpha")
mxMatrix("Full", 1, 4, TRUE, values=c(1,2,3,4), labels=c("l1", "l2", "l3", "l4"), name="beta")
mxConstraint(alpha, "=", 1-exp(beta*5))
....
Do I understand your model right, Kevin?
Log in or register to post comments
In reply to Unfortunately, "lambda1" by Ryne
Hi Ryne. So, the loadings
Hi Ryne. So, the loadings would have similar constraints - the only thing that would change is the number (in this case 5) based on the timing of the measurement occasions. For example, I would want the first factor loading to equal "1-exp(beta*1)", the second loading to equal "1-exp(beta*2)", etc.
Log in or register to post comments
In reply to Thanks. I'm still not sure if by kgrimm
One way to implement what
One way to implement what you've described is to add some MxMatrices and one MxAlgebra to your model. One of the MxMatrix objects, let's call it "leftHandSide", will be a 1x1 matrix that either has a fixed value (the factor loading from the 'A' matrix) or has a free parameter (with the same name as the factor loading from the 'A' matrix).
Then create another MxMatrix object, let's call it "l1matrix", that is a 1x1 matrix that contains the free parameter L1. Next we need a MxMatrix object, call it "one" that contains only the value 1. And a MxMatrix object, call it "five" that contains only the value 5.
Finally, the MxAlgebra statement, let's call it "rightHandSide", has the expression:
one - exp(l1matrix * five). Sorry for all the complications but algebras only work on matrix operations, not scalar operations.
Now you can add mxConstraint("leftHandSide", "=", "rightHandSide") to your model.
Log in or register to post comments
In reply to One way to implement what by mspiegel
This is a question for Mike.
This is a question for Mike. I'm unsure of the exact state of the namespaces, but at one point we had discussed allowing a free parameter to be a result of an algebra that evaluated to a 1x1 matrix.
If so, Kevin could create a 1x1 matrix called beta
and 4 algebras
and then add labels for the elements of the target matrix (or paths) as "lambda1", "lambda2", ... etc.
Is that legal as currently implemented?
Log in or register to post comments
In reply to This is a question for Mike. by Steve
We have implemented algebra
We have implemented algebra substitution and matrix substitution, whereby the result of a 1x1 algebra or matrix could be substituted into a single element of another matrix. Your example isn't using algebra or matrix substitution (because there's only one MxMatrix expression and it has only free values, where a fixed value is needed to be the target of a substitution). The example is almost valid OpenMx, except that we don't have constant substitution. We talked about implementing constant substitution (where a constant scalar or matrix was automatically converted into a MxMatrix), and free parameter substitution (where a free parameter was converted into a 1x1 matrix), it's on the TODO list.
To remember the differences between substitutions: the target of algebra substitution and matrix substitution occurs inside of MATRICES, and the target of constant substitution and free parameter substitution occurs inside of ALGEBRAS.
Log in or register to post comments
In reply to We have implemented algebra by mspiegel
Maybe I should write out the
Maybe I should write out the whole section of code, because the way I'm reading your reply, this should work:
Log in or register to post comments
In reply to Maybe I should write out the by Steve
No the "1" and the "3"
No the "1" and the "3" located in mxAlgebra(1-exp(beta*3), name="lambda3") are constants, and are (currently) not allowed in MxAlgebra statements. Only MxMatrices or other MxAlebra statements are allowed as operands.
Log in or register to post comments
In reply to Maybe I should write out the by Steve
How
How about:
Howzit?
Log in or register to post comments
In reply to How by Ryne
Yeah, that looks correct to
Yeah, that looks correct to me.
Log in or register to post comments
In reply to Yeah, that looks correct to by mspiegel
And for continuous time, or
And for continuous time, or datasets where there are extensive missing values (say each person measured only on 3 of 100 possible occasions) we might want to use definition variables in place of the 1, 2, 3 etc. to generate lambda loadings specific to that individual. Hopefully that would work as well.
Log in or register to post comments
In reply to How by Ryne
An interesting way of
An interesting way of circumventing the current limitations of mxAlgebra.
But, beta, the matrix, and beta, the parameter.... Oww! My namespace hurts.
Seriously, if we are ever to allow parameters to appear directly in algebras, this type of willful namespace abuse should throw an error, and soon.
I like the idea of using definition variables to express the exact time lag since the inception of the experiment. Not only is that useful, but in the multilevel context, it has been shown to lead to increased parameter precision.
Log in or register to post comments
In reply to An interesting way of by Steve
Oops, I didn't notice that
Oops, I didn't notice that 'beta' was used as a matrix name and free parameter name in the example. Yes, this should be disallowed. I'll fix it (maybe open a ticket and then fix it).
Log in or register to post comments
In reply to Oops, I didn't notice that by mspiegel
OK, revision 771 in the
OK, revision 771 in the repository ensures that free parameters and named entities cannot have overlapping names. I also added a new script to the test suite that shows off the "check to make sure that errors occur" function, omxCheckError(). You can see an example of it here: http://openmx.psyc.virginia.edu/dev/browser/trunk/models/passing/NameParameterOverlap.R?rev=771. I will document this function tomorrow morning.
Log in or register to post comments
Does anyone has a solution to
Does anyone has a solution to Kevin's question in the meantime? Defining a structured loading matrix according to a nonlinear target function is straightforward in Mplus see e.g. Grimm and Ram (2009). I sense that it is technically also possible to define nonlinear constraints in openMx - but is it also practically feasible?
Lit.:
Grimm, K., & Ram, N. (2009). Nonlinear growth models in Mplus and SAS. Structural Equation Modeling, 16, 676 - 701.
Log in or register to post comments
In reply to Does anyone has a solution to by prast
I believe it should be
I believe it should be straightforward to devise a general script in OpenMx, much as Jack & I did in the case of fitting non-linear growth curves to data from twins* using classic Mx. By general, I mean a script that could be fitted to datasets with differing numbers of occasions, without altering the body of the model specification. There are several ways to attack this problem, of which the mxConstraint() function might seem the most verbally similar to the forum topic. However, I think it is more efficient to use mxAlgebra() to specify the growth curve function for the predicted means, and to specify the factor loadings as the partial derivatives of the function for the factor loadings. For example, the guts of it for a logistic LGC model might look like this:
# Matrices for parameters & some constants
mxMatrix("Unit", name="u", nrow = noccasions, ncol = 1)
mxMatrix("Full", name="t", nrow = noccasions, ncol = 1, free=F, values=c(1:noccasions))
mxMatrix("Full", name="a", nrow=1, ncol=1, free=TRUE, values=.1)
mxMatrix("Full", name="i", nrow=1, ncol=1, free=TRUE, values=.1)
mxMatrix("Full", name="r", nrow=1, ncol=1, free=TRUE, values=.1)
# Logistic function for means
mxAlgebra(i %x% u + (a-i) %x% (\exp(-(t-u) %x% r), name="expMean")
#dJ/da - asymptote factor loadings
mxAlgebra((i %x% u-(\exp(-(t-u) %x% r)).(((a * i) %x% U)/J)) / J, name="K");
#dJ/di - intercept factor loadings
mxAlgebra( ( (a %x% u) - (u-(\exp(-(t-u) %x% r))). (((a * i) %x% U)/J) ) /J, name="L" )
#dJ/dr - rate factor loadings
mxAlgebra( ( ((a-i) %x% (t-u)). (\exp(-(t-u) %x% r)). (((a * i) %x% U)/J) ) / J, name="M")
# combined factor loading matrix
mxAlgebra(cbind(K,L,M), name="F")
-- except without all the typos and errors that this quick draft probably contains :). Must implement this properly soon... but HTH for now.
*Neale M.C., McArdle JJ: Structured latent growth curves for twin data. Twin Res 2000; 3:165-177 http://www.vipbg.vcu.edu/vipbg/Articles/TwinRes-structured-2000.pdf
Log in or register to post comments
In reply to I believe it should be by neale
Thank you Michael, it works
Thank you Michael, it works perfectly!
I just needed to change some minor things:
# Matrices for parameters & some constants
mxMatrix("Unit", name="u", nrow = noccasions, ncol = 1),
mxMatrix("Full", name="t", nrow = noccasions, ncol = 1,
free=FALSE, values=c(1:noccasions)),
mxMatrix("Full", name="a", nrow=1, ncol=1, free=TRUE, values=15, label="alpha"),
mxMatrix("Full", name="i", nrow=1, ncol=1, free=TRUE, values=3, label="beta", lbound=0),
mxMatrix("Full", name="r", nrow=1, ncol=1, free=TRUE, values=.5, label="gamma"),
## Logistic function:
mxAlgebra(i %x% u + (a-i) %x% exp(-(t-u) %x% r), name="J"),
## Derivatives (cf. Neale & McArdle):
#dJ/da - asymptote factor loadings: Dim 7X1
mxAlgebra(((i %x% u-(exp(-(t-u) %x% r))/(((a * i) %x% u)/J)) / J), name="K"),
#dJ/di - intercept factor loadings: Dim 7X1
mxAlgebra((( (a %x% u) - (u-(exp(-(t-u) %x% r)))/(((a * i) %x% u)/J) ) /J), name="L" ),
#dJ/dr - rate factor loadings: Dim 7X1
mxAlgebra(( ((a-i) %x% (t-u))/(exp(-(t-u) %x% r))/(((a * i) %x% u)/J) ) / J, name="W"),
# combined factor loading matrix: Dimension = obs X parameters
mxAlgebra(cbind(K,L,W), name="LM"),
### logistic function for means
mxAlgebra(t(i %x% u + (a-i) %x% exp(-(t-u) %x% r)), name="expMean"),
Log in or register to post comments
In reply to Thank you Michael, it works by prast
Try separating out common
Try separating out common subexpressions into their own algebras. OpenMx currently does not perform common subexpression elimination, which means these expressions are evaluated N times if they appear N times. One example would be: mxAlgebra( ((a * i) %x% u) / J, name = "subexpression1"). And then you could use 'subexpression1' in the other three algebras. The same with exp(-(t-u) %x% r)
Log in or register to post comments