Add thread-safe logging functions
[openmx:openmx.git] / src / omxRAMExpectation.cpp
1 /*
2  *  Copyright 2007-2013 The OpenMx Project
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *       http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  */
16
17 #include "omxExpectation.h"
18 #include "omxFitFunction.h"
19 #include "omxBLAS.h"
20 #include "omxFIMLFitFunction.h"
21 #include "omxMLFitFunction.h"
22 #include "omxRAMExpectation.h"
23
24 typedef struct {
25
26         omxMatrix *cov, *means; // observed covariance and means
27         omxMatrix *A, *S, *F, *M, *I;
28         omxMatrix *C, *EF, *ZM, *U, *V, *W, *X, *Y, *Z, *Ax, *P, *bCB, *ZSBC, *Mns, *lilI, *eCov, *beCov;
29     omxMatrix *dA, *dS, *dM, *b, *D, *paramVec;
30     // For gradients
31     omxMatrix ***eqnOuts;
32     omxMatrix **dAdts, **dSdts, **dMdts;
33
34     // Each vector keeps track of the number
35     // of non-zero elements in the
36     // corresponding dXdts matrix.
37     int *dAdtsCount, *dSdtsCount, *dMdtsCount;
38
39     int *dAdtsRowCache, *dAdtsColCache;
40     int *dSdtsRowCache, *dSdtsColCache;
41     int *dMdtsRowCache, *dMdtsColCache;
42
43     omxMatrix *tempVec, *bigSum, *lilSum;
44
45     int *pNums, nParam; // For Fast Gradient/Hessian Computation
46         int numIters;
47         double logDetObserved;
48         double n;
49         double *work;
50         int lwork;
51
52 } omxRAMExpectation;
53
54 static void calculateRAMGradientComponents(omxExpectation* oo, omxMatrix**, omxMatrix**, int*);
55 static void sliceCrossUpdate(omxMatrix* A, omxMatrix* B, int row, int col, omxMatrix* result);
56 static void omxCalculateRAMCovarianceAndMeans(omxMatrix* A, omxMatrix* S, omxMatrix* F, 
57     omxMatrix* M, omxMatrix* Cov, omxMatrix* Means, int numIters, omxMatrix* I, 
58     omxMatrix* Z, omxMatrix* Y, omxMatrix* X, omxMatrix* Ax);
59 static void fastRAMGradientML(omxExpectation* oo, omxFitFunction* off, double* result);
60 static omxMatrix* omxGetRAMExpectationComponent(omxExpectation* ox, omxFitFunction* off, const char* component);
61
62 // Speedup Helper
63 static void ADB(omxMatrix** A, omxMatrix** B, int numArgs, omxMatrix** D, int *Dcounts, 
64         int *DrowCache, int *DcolCache, int matNum, omxFreeVar* varList, 
65         int* pNums, int nParam, omxMatrix*** result) {
66     // Computes Matrix %*% params %*% Matrix in O(K^2) time.  Based on von Oertzen & Brick, in prep.
67     // Also populates the matrices called D if it appears.
68     // Minimal error checking.
69     if(OMX_DEBUG_ALGEBRA) mxLog("Beginning ADB."); //:::DEBUG:::
70
71     omxFreeVar var;  // TODO, store a pointer instead of a copy
72     int paramNo;
73
74     for(int param = 0; param < nParam; param++) {
75         if(pNums != NULL) {
76             paramNo = pNums[param];
77         } else {
78             paramNo = param;
79         }
80         var = varList[paramNo];
81
82         // Set D
83         if(D != NULL) {
84             int nonzero = 0;
85             omxMatrix *thisD = D[param];
86             memset(thisD->data, 0, sizeof(double) * thisD->cols * thisD->rows);
87             // Honestly, this should be calculated only once.
88             for(size_t varLoc = 0; varLoc < var.locations.size(); varLoc++) {
89                 if(~var.locations[varLoc].matrix == matNum) {
90                         int row = var.locations[varLoc].row;
91                         int col = var.locations[varLoc].col;
92                         omxSetMatrixElement(thisD, row, col, 1.0);
93                         if (DrowCache != NULL) DrowCache[param] = row;
94                         if (DcolCache != NULL) DcolCache[param] = col;
95                         nonzero++;
96                 }
97             }
98             Dcounts[param] = nonzero;
99         }
100         for(int eqn = 0; eqn < numArgs; eqn++) {
101             omxMatrix* thisResult = result[eqn][param];
102             memset(thisResult->data, 0, sizeof(double) * thisResult->cols * thisResult->rows);
103             for(size_t varLoc = 0; varLoc < var.locations.size(); varLoc++) {
104                 if(~var.locations[varLoc].matrix == matNum) {
105                     sliceCrossUpdate(A[eqn], B[eqn],
106                                      var.locations[varLoc].row,
107                                      var.locations[varLoc].col, thisResult);
108                 }
109             }
110         }
111     }
112 }
113
114 static void sliceCrossUpdate(omxMatrix* A, omxMatrix* B, int row, int col, omxMatrix* result) {
115     // Performs outer multiply of column col of A by row row of B.
116     // Adds product to beta * result.
117     int nrow = A->rows;
118     int ncol = B->cols;
119     int resultrows = result->rows;
120     int brows = B->rows;
121
122     if (A->colMajor && B->colMajor && result->colMajor) {
123         int aoffset = row * nrow;
124         double* Adata = A->data;
125         double* Bdata = B->data;
126         double* resdata = result->data;
127
128         for(int k = 0; k < ncol; k++) {
129             int offset = k * resultrows;
130             double factor = Bdata[k * brows + col];
131             for(int j = 0; j < nrow; j++) {
132                 resdata[offset + j] += Adata[aoffset + j] * factor;
133             }
134         }
135     } else {
136         for(int k = 0; k < ncol; k++) {
137             for(int j = 0; j < nrow; j++) {
138                 omxAccumulateMatrixElement(result, j, k, 
139                     omxMatrixElement(A, j, row) * omxMatrixElement(B, col, k));
140             }
141         }
142     }
143 }
144
145 static void omxCallRAMExpectation(omxExpectation* oo) {
146     if(OMX_DEBUG) { mxLog("RAM Expectation calculating."); }
147         omxRAMExpectation* oro = (omxRAMExpectation*)(oo->argStruct);
148         
149         omxRecompute(oro->A);
150         omxRecompute(oro->S);
151         omxRecompute(oro->F);
152         if(oro->M != NULL)
153             omxRecompute(oro->M);
154             
155         omxCalculateRAMCovarianceAndMeans(oro->A, oro->S, oro->F, oro->M, oro->cov, 
156                 oro->means, oro->numIters, oro->I, oro->Z, oro->Y, oro->X, oro->Ax);
157 }
158
159 static void omxDestroyRAMExpectation(omxExpectation* oo) {
160
161         if(OMX_DEBUG) { mxLog("Destroying RAM Expectation."); }
162         
163         omxRAMExpectation* argStruct = (omxRAMExpectation*)(oo->argStruct);
164
165         /* We allocated 'em, so we destroy 'em. */
166         if(argStruct->cov != NULL)
167                 omxFreeMatrixData(argStruct->cov);
168
169         if(argStruct->means != NULL) {
170                 omxFreeMatrixData(argStruct->dM);
171                 omxFreeMatrixData(argStruct->ZM);
172                 omxFreeMatrixData(argStruct->bCB);
173                 omxFreeMatrixData(argStruct->b);
174                 omxFreeMatrixData(argStruct->tempVec);
175                 omxFreeMatrixData(argStruct->bigSum);
176                 omxFreeMatrixData(argStruct->lilSum);
177                 omxFreeMatrixData(argStruct->beCov);
178                 omxFreeMatrixData(argStruct->means);
179         }
180
181         int nParam = argStruct->nParam;
182         if(nParam >= 0) {
183                 for(int j = 0; j < nParam; j++) {
184                         if(argStruct->dAdts != NULL)
185                                 omxFreeMatrixData(argStruct->dAdts[j]);
186                         if(argStruct->dSdts != NULL)
187                                 omxFreeMatrixData(argStruct->dSdts[j]);
188                         if(argStruct->dMdts != NULL) 
189                                 omxFreeMatrixData(argStruct->dMdts[j]);
190                         if(argStruct->eqnOuts != NULL)
191                                 omxFreeMatrixData(argStruct->eqnOuts[0][j]);
192                 }
193                 omxFreeMatrixData(argStruct->paramVec);
194         }
195
196         if (argStruct->dA != NULL)
197                 omxFreeMatrixData(argStruct->dA);
198
199         if (argStruct->dS != NULL)
200                 omxFreeMatrixData(argStruct->dS);
201
202         omxFreeMatrixData(argStruct->I);
203         omxFreeMatrixData(argStruct->lilI);
204         omxFreeMatrixData(argStruct->X);
205         omxFreeMatrixData(argStruct->Y);
206         omxFreeMatrixData(argStruct->Z);
207         omxFreeMatrixData(argStruct->Ax);
208
209         omxFreeMatrixData(argStruct->W);
210         omxFreeMatrixData(argStruct->U);
211         omxFreeMatrixData(argStruct->EF);
212         omxFreeMatrixData(argStruct->V);
213         omxFreeMatrixData(argStruct->ZSBC);
214         omxFreeMatrixData(argStruct->C);
215         omxFreeMatrixData(argStruct->eCov);
216
217 }
218
219 static void omxPopulateRAMAttributes(omxExpectation *oo, SEXP algebra) {
220     if(OMX_DEBUG) { mxLog("Populating RAM Attributes."); }
221
222         omxRAMExpectation* oro = (omxRAMExpectation*) (oo->argStruct);
223         omxMatrix* A = oro->A;
224         omxMatrix* S = oro->S;
225         omxMatrix* X = oro->X;
226         omxMatrix* Ax= oro->Ax;
227         omxMatrix* Z = oro->Z;
228         omxMatrix* I = oro->I;
229     int numIters = oro->numIters;
230     double oned = 1.0, zerod = 0.0;
231     
232     omxRecompute(A);
233     omxRecompute(S);
234         
235         omxShallowInverse(numIters, A, Z, Ax, I ); // Z = (I-A)^-1
236         
237         if(OMX_DEBUG_ALGEBRA) { mxLog("....DGEMM: %c %c \n %d %d %d \n %f \n %x %d %x %d \n %f %x %d.", *(Z->majority), *(S->majority), (Z->rows), (S->cols), (S->rows), oned, Z->data, (Z->leading), S->data, (S->leading), zerod, Ax->data, (Ax->leading));}
238         // F77_CALL(omxunsafedgemm)(Z->majority, S->majority, &(Z->rows), &(S->cols), &(S->rows), &oned, Z->data, &(Z->leading), S->data, &(S->leading), &zerod, Ax->data, &(Ax->leading));     // X = ZS
239         omxDGEMM(FALSE, FALSE, oned, Z, S, zerod, Ax);
240
241         if(OMX_DEBUG_ALGEBRA) { mxLog("....DGEMM: %c %c %d %d %d %f %x %d %x %d %f %x %d.", *(Ax->majority), *(Z->minority), (Ax->rows), (Z->rows), (Z->cols), oned, X->data, (X->leading), Z->data, (Z->lagging), zerod, Ax->data, (Ax->leading));}
242         // F77_CALL(omxunsafedgemm)(Ax->majority, Z->minority, &(Ax->rows), &(Z->rows), &(Z->cols), &oned, Ax->data, &(Ax->leading), Z->data, &(Z->leading), &zerod, Ax->data, &(Ax->leading)); 
243         omxDGEMM(FALSE, TRUE, oned, Ax, Z, zerod, Ax);
244         // Ax = ZSZ' = Covariance matrix including latent variables
245         
246         SEXP expCovExt;
247         PROTECT(expCovExt = allocMatrix(REALSXP, Ax->rows, Ax->cols));
248         for(int row = 0; row < Ax->rows; row++)
249                 for(int col = 0; col < Ax->cols; col++)
250                         REAL(expCovExt)[col * Ax->rows + row] =
251                                 omxMatrixElement(Ax, row, col);
252         setAttrib(algebra, install("UnfilteredExpCov"), expCovExt);
253         UNPROTECT(1);
254 }
255
256 /*
257  * omxCalculateRAMCovarianceAndMeans
258  *                      Just like it says on the tin.  Calculates the mean and covariance matrices
259  * for a RAM model.  M is the number of total variables, latent and manifest. N is
260  * the number of manifest variables.
261  *
262  * params:
263  * omxMatrix *A, *S, *F         : matrices as specified in the RAM model.  MxM, MxM, and NxM
264  * omxMatrix *M                         : vector containing model implied means. 1xM
265  * omxMatrix *Cov                       : On output: model-implied manifest covariance.  NxN.
266  * omxMatrix *Means                     : On output: model-implied manifest means.  1xN.
267  * int numIterations            : Precomputed number of iterations of taylor series expansion.
268  * omxMatrix *I                         : Identity matrix.  If left NULL, will be populated.  MxM.
269  * omxMatrix *Z                         : On output: Computed (I-A)^-1. MxM.
270  * omxMatrix *Y, *X, *Ax        : Space for computation. NxM, NxM, MxM.  On exit, populated.
271  */
272
273 static void omxCalculateRAMCovarianceAndMeans(omxMatrix* A, omxMatrix* S, omxMatrix* F, 
274         omxMatrix* M, omxMatrix* Cov, omxMatrix* Means, int numIters, omxMatrix* I, 
275         omxMatrix* Z, omxMatrix* Y, omxMatrix* X, omxMatrix* Ax) {
276         
277         if(OMX_DEBUG) { mxLog("Running RAM computation with numIters is %d\n.", numIters); }
278                 
279         double oned = 1.0, zerod=0.0;
280         
281         if(Ax == NULL || I == NULL || Z == NULL || Y == NULL || X == NULL) {
282                 error("Internal Error: RAM Metadata improperly populated.  Please report this to the OpenMx development team.");
283         }
284                 
285         if(Cov == NULL && Means == NULL) {
286                 return; // We're not populating anything, so why bother running the calculation?
287         }
288         
289         // if(   (Cov->rows != Cov->cols)  || (A->rows  != A->cols)  // Conformance check
290         //      || (X->rows  != Cov->cols)  || (X->cols  != A->rows)
291         //      || (Y->rows  != Cov->cols)  || (Y->cols  != A->rows)
292         //      || (Ax->rows != Cov->cols)  || (Ax->cols != A->rows)
293         //      || (I->rows  != Cov->cols)  || (I->cols  != Cov->rows)
294         //      || (Y->rows  != Cov->cols)  || (Y->cols  != A->rows)
295         //      || (M->cols  != Cov->cols)  || (M->rows  != 1)
296         //      || (Means->rows != 1)       || (Means->cols != Cov->cols) ) {
297         //              error("INTERNAL ERROR: Incorrectly sized matrices being passed to omxRAMExpectation Calculation.\n Please report this to the OpenMx development team.");
298         // }
299         
300         omxShallowInverse(numIters, A, Z, Ax, I );
301
302         if (isErrorRaised(A->currentState)) return;
303         
304         /* Cov = FZSZ'F' */
305         if(OMX_DEBUG_ALGEBRA) { mxLog("....DGEMM: %c %c %d %d %d %f %x %d %x %d %f %x %d.", *(F->majority), *(Z->majority), (F->rows), (Z->cols), (Z->rows), oned, F->data, (F->leading), Z->data, (Z->leading), zerod, Y->data, (Y->leading));}
306         // F77_CALL(omxunsafedgemm)(F->majority, Z->majority, &(F->rows), &(Z->cols), &(Z->rows), &oned, F->data, &(F->leading), Z->data, &(Z->leading), &zerod, Y->data, &(Y->leading));       // Y = FZ
307         omxDGEMM(FALSE, FALSE, 1.0, F, Z, 0.0, Y);
308
309         if(OMX_DEBUG_ALGEBRA) { mxLog("....DGEMM: %c %c %d %d %d %f %x %d %x %d %f %x %d.", *(Y->majority), *(S->majority), (Y->rows), (S->cols), (S->rows), oned, Y->data, (Y->leading), S->data, (S->leading), zerod, X->data, (X->leading));}
310         // F77_CALL(omxunsafedgemm)(Y->majority, S->majority, &(Y->rows), &(S->cols), &(S->rows), &oned, Y->data, &(Y->leading), S->data, &(S->leading), &zerod, X->data, &(X->leading));       // X = FZS
311         omxDGEMM(FALSE, FALSE, 1.0, Y, S, 0.0, X);
312
313         if(OMX_DEBUG_ALGEBRA) { mxLog("....DGEMM: %c %c %d %d %d %f %x %d %x %d %f %x %d.", *(X->majority), *(Y->minority), (X->rows), (Y->rows), (Y->cols), oned, X->data, (X->leading), Y->data, (Y->lagging), zerod, Cov->data, (Cov->leading));}
314         // F77_CALL(omxunsafedgemm)(X->majority, Y->minority, &(X->rows), &(Y->rows), &(Y->cols), &oned, X->data, &(X->leading), Y->data, &(Y->leading), &zerod, Cov->data, &(Cov->leading));
315         omxDGEMM(FALSE, TRUE, 1.0, X, Y, 0.0, Cov);
316          // Cov = FZSZ'F' (Because (FZ)' = Z'F')
317         
318         if(OMX_DEBUG_ALGEBRA) {omxPrintMatrix(Cov, "....RAM: Model-implied Covariance Matrix:");}
319         
320         if(M != NULL && Means != NULL) {
321                 // F77_CALL(omxunsafedgemv)(Y->majority, &(Y->rows), &(Y->cols), &oned, Y->data, &(Y->leading), M->data, &onei, &zerod, Means->data, &onei);
322                 omxDGEMV(FALSE, 1.0, Y, M, 0.0, Means);
323                 if(OMX_DEBUG_ALGEBRA) {omxPrintMatrix(Means, "....RAM: Model-implied Means Vector:");}
324         }
325 }
326
327 void omxInitRAMExpectation(omxExpectation* oo) {
328         
329         omxState* currentState = oo->currentState;      
330         SEXP rObj = oo->rObj;
331
332         if(OMX_DEBUG) { mxLog("Initializing RAM expectation."); }
333         
334         int l, k;
335
336         SEXP slotValue;
337         
338         omxRAMExpectation *RAMexp = (omxRAMExpectation*) R_alloc(1, sizeof(omxRAMExpectation));
339         
340         /* Set Expectation Calls and Structures */
341         oo->computeFun = omxCallRAMExpectation;
342         oo->destructFun = omxDestroyRAMExpectation;
343         oo->componentFun = omxGetRAMExpectationComponent;
344         oo->populateAttrFun = omxPopulateRAMAttributes;
345         oo->argStruct = (void*) RAMexp;
346         
347         /* Set up expectation structures */
348         if(OMX_DEBUG) { mxLog("Initializing RAM expectation."); }
349
350         if(OMX_DEBUG) { mxLog("Processing M."); }
351         RAMexp->M = omxNewMatrixFromSlot(rObj, currentState, "M");
352
353         if(OMX_DEBUG) { mxLog("Processing A."); }
354         RAMexp->A = omxNewMatrixFromSlot(rObj, currentState, "A");
355
356         if(OMX_DEBUG) { mxLog("Processing S."); }
357         RAMexp->S = omxNewMatrixFromSlot(rObj, currentState, "S");
358
359         if(OMX_DEBUG) { mxLog("Processing F."); }
360         RAMexp->F = omxNewMatrixFromSlot(rObj, currentState, "F");
361
362         /* Identity Matrix, Size Of A */
363         if(OMX_DEBUG) { mxLog("Generating I."); }
364         RAMexp->I = omxNewIdentityMatrix(RAMexp->A->rows, currentState);
365         omxRecompute(RAMexp->I);
366         RAMexp->lilI = omxNewIdentityMatrix(RAMexp->F->rows, currentState);
367         omxRecompute(RAMexp->lilI);
368         
369
370         if(OMX_DEBUG) { mxLog("Processing expansion iteration depth."); }
371         PROTECT(slotValue = GET_SLOT(rObj, install("depth")));
372         RAMexp->numIters = INTEGER(slotValue)[0];
373         if(OMX_DEBUG) { mxLog("Using %d iterations.", RAMexp->numIters); }
374         UNPROTECT(1);
375
376         l = RAMexp->F->rows;
377         k = RAMexp->A->cols;
378
379         if(OMX_DEBUG) { mxLog("Generating internals for computation."); }
380
381         RAMexp->Z =     omxInitMatrix(NULL, k, k, TRUE, currentState);
382         RAMexp->Ax =    omxInitMatrix(NULL, k, k, TRUE, currentState);
383         RAMexp->W =     omxInitMatrix(NULL, k, k, TRUE, currentState);
384         RAMexp->U =     omxInitMatrix(NULL, l, k, TRUE, currentState);
385         RAMexp->Y =     omxInitMatrix(NULL, l, k, TRUE, currentState);
386         RAMexp->X =     omxInitMatrix(NULL, l, k, TRUE, currentState);
387         RAMexp->EF=     omxInitMatrix(NULL, k, l, TRUE, currentState);
388         RAMexp->V =     omxInitMatrix(NULL, l, k, TRUE, currentState);
389         RAMexp->ZSBC =  omxInitMatrix(NULL, k, l, TRUE, currentState);
390         RAMexp->C =     omxInitMatrix(NULL, l, l, TRUE, currentState);
391         
392         if(omxIsMatrix(RAMexp->A)) {
393                 RAMexp->dA = omxInitMatrix(NULL, k, k, TRUE, currentState);
394         } else {
395                 RAMexp->dA = NULL;
396         }
397         if(omxIsMatrix(RAMexp->S)) {
398                 RAMexp->dS = omxInitMatrix(NULL, k, k, TRUE, currentState);
399         } else {
400                 RAMexp->dS = NULL;
401         }
402         if(RAMexp->M != NULL) {
403                 RAMexp->dM =    omxInitMatrix(NULL, 1, k, TRUE, currentState);
404                 RAMexp->ZM =    omxInitMatrix(NULL, k, 1, TRUE, currentState);
405                 RAMexp->bCB =   omxInitMatrix(NULL, k, 1, TRUE, currentState);
406                 RAMexp->b =     omxInitMatrix(NULL, l, 1, TRUE, currentState);
407                 RAMexp->tempVec =       omxInitMatrix(NULL, k, 1, TRUE, currentState);
408                 RAMexp->bigSum =        omxInitMatrix(NULL, k, 1, TRUE, currentState);
409                 RAMexp->lilSum =        omxInitMatrix(NULL, l, 1, TRUE, currentState);
410                 RAMexp->beCov =         omxInitMatrix(NULL, l, 1, TRUE, currentState);
411                 RAMexp->Mns =   NULL;
412     }
413
414         RAMexp->cov =           omxInitMatrix(NULL, l, l, TRUE, currentState);
415
416         if(RAMexp->M != NULL) {
417                 RAMexp->means =         omxInitMatrix(NULL, 1, l, TRUE, currentState);
418         } else {
419             RAMexp->means  =    NULL;
420     }
421
422     // Derivative space:
423         RAMexp->eqnOuts = NULL;
424         RAMexp->dAdts = NULL;
425         RAMexp->dSdts = NULL;
426         RAMexp->dMdts = NULL;
427         RAMexp->paramVec = NULL;
428         RAMexp->D =     NULL;
429         RAMexp->pNums = NULL;
430         RAMexp->nParam = -1;
431         RAMexp->eCov=       omxInitMatrix(NULL, l, l, TRUE, currentState);
432
433 }
434
435 /*
436  * fastRAMGradientML
437  *                      Calculates the derivatives of the likelihood for a RAM model.  
438  * Locations of free parameters are extracted from the omxExpecation's state object.
439  *
440  * params:
441  * omxMatrix *A, *S, *F         : matrices as specified in the RAM model.  MxM, MxM, and NxM
442  * omxMatrix *M                         : vector containing model implied means. 1xM
443  * omxMatrix *Cov                       : On output: model-implied manifest covariance.  NxN.
444  * omxMatrix *Means                     : On output: model-implied manifest means.  1xN.
445  * int numIterations            : Precomputed number of iterations of taylor series expansion.
446  * omxMatrix *I                         : Identity matrix.  If left NULL, will be populated.  MxM.
447  * omxMatrix *Z                         : On output: Computed (I-A)^-1. MxM.
448  * omxMatrix *Y, *X, *Ax        : Space for computation. NxM, NxM, MxM.  On exit, populated.
449  */
450
451 static void fastRAMGradientML(omxExpectation* oo, omxFitFunction* off, double* result) {
452     
453     // This calculation is based on von Oertzen and Brick, in prep.
454     //
455     // Steps:
456     // 1) (Re) Calculate current A, S, F, M, and Z (where Z = (I-A)^-1)
457     // 2) cov = current Expected Covariance (fxf)
458     // 3) eCov = cov^(-1) (fxf) *
459     // 4) ZS = Z S  (axa) *
460     // 5) B = F Z   (fxa) *
461     // 6) C = I - eCov D, where D is the observed covariance matrix (fxf) *
462     // 7) BC = B^T C (axf) *
463     // 8) ZSB = ZS (B^T) *
464     // 8) ZSBC = ZS BC (axf) *
465     // 9) eCovB = cov^(-1) B (fxa) *
466     // For Means:
467         // 10) Means = BM (fx1) *
468         // 11) b = Means - d, where d is the observed means matrix (fx1)
469         // 12) beCov = b cov^(-1) (1xf) *
470         // 13) beCovB = b cov^(-1) B (1xa) *
471
472     
473     // 14) For each location in locations: (Note: parallelize, gradients here!)
474     //      1A) Calculate dA/dt and dS/dt by substituting 1s into empty matrices
475     //      1B) eqnList = 2 * ADB(eCovB, dAdt, ZSBC)
476     //      1C) eqnList2 = ADB(eCovB, dSdt, BC)  // Preferably, build
477     //      2) sum = eqnList1 + eqnList2
478     //      If Means:
479     //          3) sumVec = beCovB dAdt ZS
480     //          4) sumVec += beCovB ZS dAdt
481     //          5) sumVec += beCovB dSdt
482     //          6) sumVec *= B^T
483     //          7) sumVec += 2 * B dAdt Means
484     //          8) sumVec += 2 * B dMdt
485     //          9) sum += sumVec beCov
486
487     //  Right.  All the * elements are saved for hessian computation.
488  
489     if(OMX_DEBUG) {mxLog("Calculating fast RAM/ML gradient."); }
490     
491     omxRAMExpectation* oro = (omxRAMExpectation*)(oo->argStruct);
492                                     // Size reference: A is axa, F is fxa
493     omxMatrix* A = oro->A;          // axa
494     omxMatrix* S = oro->S;          // axa
495     omxMatrix* F = oro->F;          // fxa
496     omxMatrix* M = oro->M;          // 1xa
497     omxMatrix* Z = oro->Z;          // axa
498
499     omxMatrix* cov = oro->cov;      // fxf
500     omxMatrix* means = oro->means;  // fx1
501     omxMatrix* eCov = oro->eCov;    // fxf
502
503         omxMatrix* ZS = oro->Ax;        // axa
504         
505     omxMatrix* B = oro->X;          // fxa
506     omxMatrix* C = oro->C;          // fxf
507     omxMatrix* BC = oro->EF;        // fxa
508     omxMatrix* ZSBC = oro->ZSBC;    // fxa
509     omxMatrix* eCovB = oro->U;      // fxa
510
511     omxMatrix* ZM = oro->ZM;        // 1xa
512     omxMatrix* beCov = oro->beCov;  // 1xf
513     omxMatrix* bCB = oro->bCB;      // 1xa
514     omxMatrix* b = oro->b;          // 1xa
515     
516     omxMatrix** dAdts = oro->dAdts;
517     omxMatrix** dSdts = oro->dSdts;
518     omxMatrix** dMdts = oro->dMdts;
519     
520     int* dAdtsCount = oro->dAdtsCount;
521     int* dSdtsCount = oro->dSdtsCount;
522     int* dMdtsCount = oro->dMdtsCount;
523
524     int* dAdtsRowCache = oro->dAdtsRowCache;
525     int* dAdtsColCache = oro->dAdtsColCache;
526     int* dSdtsRowCache = oro->dSdtsRowCache;
527     int* dSdtsColCache = oro->dSdtsColCache;
528     int* dMdtsRowCache = oro->dMdtsRowCache;
529     int* dMdtsColCache = oro->dMdtsColCache;
530
531     omxMatrix*** eqnOuts = oro->eqnOuts;
532     
533     omxMatrix* tempVec = oro->tempVec;
534     omxMatrix* bigSum  = oro->bigSum;
535     omxMatrix* lilSum  = oro->lilSum;
536     omxMatrix* D = oro->D;
537     omxMatrix* Mns = oro->Mns;
538     
539     omxMatrix* lilI = oro->lilI;
540     omxMatrix* paramVec = oro->paramVec;
541     
542     double n = oro->n;
543     
544     int* pNums = oro->pNums;
545     int nParam = oro->nParam;
546     
547     int Amat = A->matrixNumber;
548     int Smat = S->matrixNumber;
549     int Mmat = 0;
550     double oned = 1.0, minusoned = -1.0;
551     int onei = 1;
552     int info;
553     char u = 'U';
554     if(M != NULL) Mmat = M->matrixNumber;
555     
556     omxFreeVar* varList = oo->currentState->freeVarList;
557
558     omxMatrix *eqnList1[1], *eqnList2[1];
559
560     if(nParam < 0) {
561         nParam = 0;
562         int nTotalParams = oo->currentState->numFreeParams;
563         if(OMX_DEBUG) { mxLog("Planning Memory for Fast Gradient Calculation: Using %d params.", nParam); }
564         unsigned short int calc[nTotalParams]; 
565         // Work out the set of parameters for which we can calculate gradients
566         // TODO: Potential speedup by splitting this to calculate dA, dS, and dM separately
567         for(int parm = 0; parm < nTotalParams; parm++) {
568             omxFreeVar ofv = varList[parm];
569             calc[parm] = 0;
570             for(size_t loc = 0; loc < ofv.locations.size(); loc++) {
571                 int varMat = ~(ofv.locations[loc].matrix);
572                 if(varMat == Amat || varMat == Smat || (M != NULL && varMat == Mmat)) {
573                     calc[parm] = 1;
574                 } else {
575                     calc[parm] = 0;
576                     break;
577                 }
578             }
579             if(calc[parm]) {
580                 nParam++;
581             }
582         }
583         oro->nParam = nParam;
584         pNums = (int*)R_alloc(nParam, sizeof(int));
585
586         int nextFree = 0;
587         // Populate pNums with numbers of the parameters to calculate
588         for(int parm = 0; parm < nTotalParams && nextFree < nParam; parm++) {
589             if(calc[parm]) {
590                 pNums[nextFree] = parm;
591                 nextFree++;
592
593             }
594         }
595         oro->pNums = pNums;
596         
597         oro->dAdts = (omxMatrix**) R_alloc(nParam, sizeof(omxMatrix*));
598         oro->dSdts = (omxMatrix**) R_alloc(nParam, sizeof(omxMatrix*));
599         oro->dMdts = (omxMatrix**) R_alloc(nParam, sizeof(omxMatrix*));
600         oro->dAdtsCount = (int*) R_alloc(nParam, sizeof(int));
601         oro->dSdtsCount = (int*) R_alloc(nParam, sizeof(int));
602         oro->dMdtsCount = (int*) R_alloc(nParam, sizeof(int));
603         
604         oro->dAdtsRowCache = (int*) R_alloc(nParam, sizeof(int));
605         oro->dAdtsColCache = (int*) R_alloc(nParam, sizeof(int));
606         oro->dSdtsRowCache = (int*) R_alloc(nParam, sizeof(int));
607         oro->dSdtsColCache = (int*) R_alloc(nParam, sizeof(int));
608         oro->dMdtsRowCache = (int*) R_alloc(nParam, sizeof(int));
609         oro->dMdtsColCache = (int*) R_alloc(nParam, sizeof(int));
610
611         oro->eqnOuts = (omxMatrix***) R_alloc(1, sizeof(omxMatrix**));
612         oro->eqnOuts[0] = (omxMatrix**) R_alloc(nParam, sizeof(omxMatrix*));
613         
614         int a = A->rows;
615         omxState* currentState = oo->currentState;
616         for(int i = 0; i < nParam; i++) {
617             oro->dAdts[i] = omxInitMatrix(NULL, a, a, TRUE, currentState);
618             oro->dSdts[i] = omxInitMatrix(NULL, a, a, TRUE, currentState);
619             oro->dMdts[i] = omxInitMatrix(NULL, 1, a, TRUE, currentState);
620             
621             oro->eqnOuts[0][i] = omxInitMatrix(NULL, a, a, TRUE, currentState);
622         }
623         oro->paramVec = omxInitMatrix(NULL, nParam, 1, TRUE, currentState);
624         paramVec = oro->paramVec;
625         dAdts = oro->dAdts;
626         dSdts = oro->dSdts;
627         dMdts = oro->dMdts;
628         dAdtsCount = oro->dAdtsCount;
629         dSdtsCount = oro->dSdtsCount;
630         dMdtsCount = oro->dMdtsCount;
631         
632         dAdtsRowCache = oro->dAdtsRowCache;
633         dAdtsColCache = oro->dAdtsColCache;
634         dSdtsRowCache = oro->dSdtsRowCache;
635         dSdtsColCache = oro->dSdtsColCache;
636         dMdtsRowCache = oro->dMdtsRowCache;
637         dMdtsColCache = oro->dMdtsColCache;
638
639         eqnOuts = oro->eqnOuts;
640     }
641
642     double covInfluence[nParam];
643     double meanInfluence[nParam];
644     
645     // 1) (Re) Calculate current A, S, F, M, and Z (where Z = (I-A)^-1)
646     // 2) cov = current Expected Covariance (fxf)
647     // Theoretically, we should calculate current fit function values 
648     // But we can safely assume this has already been done
649     // That assumption may no longer be valid in FIML.
650
651     // 3) eCov = cov^(-1) (fxf)
652     omxCopyMatrix(eCov, cov);                           // But expected cov is destroyed in inversion
653     
654     F77_CALL(dpotrf)(&u, &(eCov->cols), eCov->data, &(eCov->cols), &info);
655
656     if(OMX_DEBUG_ALGEBRA) { mxLog("Info on LU Decomp: %d", info);}
657     if(info > 0) {
658             char *errstr = (char*) calloc(250, sizeof(char));
659         sprintf(errstr, "Expected covariance matrix is non-positive-definite");
660         if(oo->currentState->computeCount <= 0) {
661             strncat(errstr, " at starting values", 20);
662         }
663         strncat(errstr, ".\n", 3);
664         omxRaiseError(oo->currentState, -1, errstr);                        // Raise error
665         free(errstr);
666         return;                                                                     // Leave output untouched
667     }
668     
669     F77_CALL(dpotri)(&u, &(eCov->rows), eCov->data, &(eCov->cols), &info);
670     if(info > 0) {
671             char *errstr = (char*) calloc(250, sizeof(char));
672         sprintf(errstr, "Expected covariance matrix is not invertible");
673         if(oo->currentState->computeCount <= 0) {
674             strncat(errstr, " at starting values", 20);
675         }
676         strncat(errstr, ".\n", 3);
677         omxRaiseError(oo->currentState, -1, errstr);                        // Raise error
678         free(errstr);
679         return;
680     }
681
682     // 4) ZS = Z S  (axa) *
683     omxDSYMM(FALSE, 1.0, S, Z, 0.0, ZS);
684     omxDGEMM(FALSE, FALSE, 1.0, Z, S, 0.0, ZS);
685     
686     // 5) B = F Z   (fxa) *
687     omxDGEMM(FALSE, FALSE, 1.0, F, Z, 0.0, B);
688     
689     // 6) C = I - eCov D, where D is the observed covariance matrix (fxf)
690     omxCopyMatrix(C, lilI);
691     omxDSYMM(TRUE, -1.0, eCov, D, 1.0, C);
692
693     
694     // 7) BC = B^T C (axf) *
695     omxDGEMM(TRUE, FALSE, 1.0, B, C, 0.0, BC);
696     
697     // 8) ZSBC = ZS BC (axf) *
698     omxDGEMM(FALSE, FALSE, 1.0, ZS, BC, 0.0, ZSBC);
699     
700     // 9) eCovB = cov^(-1) B (fxa) *
701     omxDSYMM(TRUE, 1.0, eCov, B, 0.0, eCovB);
702     
703     if(M != NULL) {
704         // 10) Means = BM (fx1) *
705         // This is calculated during expectation computation.
706         // 11) b = Means - d, where d is the observed means matrix (fx1)
707
708         omxCopyMatrix(b, Mns);
709         F77_CALL(daxpy)(&(means->cols), &minusoned, means->data, &onei, b->data, &onei);
710     
711         // 11) beCov = b cov^(-1) (1xf) *
712         omxDSYMV(1.0, eCov, b, 0.0, beCov);
713         
714         // 12) beCovB = b cov^(-1) B (1xa) *
715         omxDGEMV(TRUE, 1.0, eCovB, b, 0.0, bCB);
716         
717         // 13) ZM = Z %*% M (1xa)
718         omxDGEMV(FALSE, 1.0, Z, M, 0.0, ZM);
719     }
720     
721     // 14) For each location in locations:
722     //      1A) Calculate dA/dt, dS/dt, and dM/dt by substituting 1s into empty matrices
723     //      1B) 1 = 2 * ADB(eCovB, dAdt, ZSBC)
724     
725     eqnList1[0] = eCovB;
726     eqnList2[0] = ZSBC;
727     ADB(eqnList1, eqnList2, 1, dAdts, dAdtsCount, dAdtsRowCache, dAdtsColCache, 
728         Amat, varList, pNums, nParam, eqnOuts);
729     omxMatrixTrace(eqnOuts[0], nParam, paramVec);
730     
731     for(int i = 0; i < nParam; i++)
732         covInfluence[i] = 2 * omxVectorElement(paramVec, i);
733         
734     
735     //      1C) eqnList2 = ADB(eCovB, dSdt, BC)
736     eqnList1[0] = eCovB;
737     eqnList2[0] = BC;
738     ADB(eqnList1, eqnList2, 1, dSdts, dSdtsCount, dSdtsRowCache, dSdtsColCache, 
739         Smat, varList, pNums, nParam, eqnOuts);
740     omxMatrixTrace(eqnOuts[0], nParam, paramVec);
741
742     //      2) sum = eqnList1 + eqnList2
743     F77_CALL(daxpy)(&nParam, &oned, paramVec->data, &onei, covInfluence, &onei);
744
745     //      If Means:
746     if(M != NULL) {
747
748         //      Just populate dMdts
749         ADB(NULL, NULL, 0, dMdts, dMdtsCount, dMdtsRowCache, dMdtsColCache,
750             Mmat, varList, pNums, nParam, NULL);
751
752         //  This needs to be done one-per-param.
753         //  Parallelize here.
754         for(int pNum = 0; pNum < nParam; pNum++) {
755
756             switch(dAdtsCount[pNum]) {
757                 case 0:
758                     switch(dSdtsCount[pNum]) {
759                         case 0:
760                             memset(bigSum->data, 0, sizeof(double) * bigSum->rows);
761                             break;
762                         case 1:
763                         {
764                             int rowCache = dSdtsRowCache[pNum];
765                             int colCache = dSdtsColCache[pNum];
766                             memset(bigSum->data, 0, sizeof(double) * bigSum->rows);
767                             bigSum->data[colCache] = bCB->data[rowCache];                            
768                             break;
769                         }
770                         default:
771                             // 5) sumVec = beCovB dSdt (1xa)
772                             omxDGEMV(TRUE, 1.0, dSdts[pNum], bCB, 0.0, bigSum);
773                     }
774                     break;
775                 case 1:
776                 {
777                     int rowCache = dAdtsRowCache[pNum];
778                     int colCache = dAdtsColCache[pNum];
779                     double value = bCB->data[rowCache];
780
781                     int len = bCB->rows;
782                     int nrow = ZS->rows;
783                     int ncol = ZS->cols;
784
785                     if (ZS->colMajor) 
786                         for(int offset = 0; offset < len; offset++)
787                             bigSum->data[offset] = ZS->data[offset * nrow + colCache] * value;
788                     else
789                         for(int offset = 0; offset < len; offset++)
790                             bigSum->data[offset] = ZS->data[colCache * ncol + offset] * value;
791
792                     // Term with dSigma/dTheta
793                     //          4) sumVec += beCovB ZS dAdt (1xa)
794                     omxDGEMV(TRUE, 1.0, ZS, bCB, 0.0, tempVec);
795                     bigSum->data[colCache] += tempVec->data[rowCache];
796
797                     if (dSdtsCount[pNum]) {
798                         //  5) sumVec = beCovB dSdt (1xa)
799                         omxDGEMV(TRUE, 1.0, dSdts[pNum], bCB, 1.0, bigSum);
800                     }               
801                     break;
802                 }
803                 default:
804                     //          3) sumVec = beCovB dAdt ZS (1xa)
805                     omxDGEMV(TRUE, 1.0, dAdts[pNum], bCB, 0.0, tempVec);
806                     omxDGEMV(TRUE, 1.0, ZS, tempVec, 0.0, bigSum);
807                     // Term with dSigma/dTheta
808                     //          4) sumVec += beCovB ZS dAdt (1xa)
809                     omxDGEMV(TRUE, 1.0, ZS, bCB, 0.0, tempVec);
810                     omxDGEMV(TRUE, 1.0, dAdts[pNum], tempVec, 1.0, bigSum);
811                     if (dSdtsCount[pNum]) {
812                         //  5) sumVec = beCovB dSdt (1xa)
813                         omxDGEMV(TRUE, 1.0, dSdts[pNum], bCB, 1.0, bigSum);
814                     }
815             }
816  
817             //          6) sumVec *= B^T --> 1xf
818             omxDGEMV(FALSE, 1.0, B, bigSum, 0.0, lilSum);
819
820             // Factorizing dMudt
821             //          7) sumVec += 2 * B dAdt ZM (1xa)
822             //              Note: in the paper, expected manifest means are Bm
823             switch(dAdtsCount[pNum]) {
824                 case 0:
825                     break;
826                 case 1:
827                 {
828                     int rowCache = dAdtsRowCache[pNum];
829                     int colCache = dAdtsColCache[pNum];
830                     double value = ZM->data[colCache];
831
832                     int len = B->rows;
833                     int nrow = B->rows;
834                     int ncol = B->cols;
835
836                     if (B->colMajor) 
837                         for(int offset = 0; offset < len; offset++)
838                             lilSum->data[offset] += 2.0 * B->data[rowCache * nrow + offset] * value;
839                     else
840                         for(int offset = 0; offset < len; offset++)
841                             lilSum->data[offset] += 2.0 * B->data[offset * ncol + rowCache] * value;
842
843                     break;
844                 }
845                 default:
846                     omxDGEMV(FALSE, 1.0, dAdts[pNum], ZM, 0.0, tempVec);
847                     omxDGEMV(FALSE, 2.0, B, tempVec, 1.0, lilSum); // :::DEBUG:::<-- Save B %*% tempVec
848             }
849
850             //          8) sumVec += 2 * B dMdt (1xf)
851             switch(dMdtsCount[pNum]) {
852                 case 0:
853                     break;
854                 case 1: 
855                 {
856                     int moffset, boffset, nrow;
857
858                     moffset = dMdtsColCache[pNum];
859                     nrow = B->rows;
860                     boffset = moffset * nrow;
861
862                     for(int row = 0; row < nrow; row++)
863                         lilSum->data[row] += 2.0 * B->data[boffset + row];
864
865                     break;
866                 }
867                 default:
868                     omxDGEMV(FALSE, 2.0, B, dMdts[pNum], 1.0, lilSum); // :::DEBUG::: <-- Save B %*% dM
869             }
870
871             //          9) sum = sumVec beCov (1x1)
872             int len = lilSum->rows * lilSum->cols;
873             meanInfluence[pNum] = F77_CALL(ddot)(&len, lilSum->data, &onei, beCov->data, &onei);
874         }
875             
876     } else { 
877         for(int i = 0; i < nParam; i++)
878             meanInfluence[i] = 0;
879     }
880     
881     for(int pNum = 0; pNum < nParam; pNum++) {
882         int nextP = pNums[pNum]; // Original varList parameter number
883         result[pNum] = (covInfluence[pNum] * (n-1)) - (meanInfluence[pNum] * n);
884         if(OMX_DEBUG) {
885             mxLog("Revised calculation for Gradient value %d: Cov: %3.9f, Mean: %3.9f, total: %3.9f",
886                 nextP, covInfluence[pNum], meanInfluence[pNum], result[pNum]);
887         }
888     }
889     
890     // Phew.
891
892 }
893
894
895 static void calculateRAMGradientComponents(omxExpectation* oo, omxMatrix** dSigmas, omxMatrix** dMus, int* status) {
896
897     // Steps:
898     // 1) (Re) Calculate current A, S, F, and Z (where Z = (I-A)^-1)
899     // 2) E = Z S Z^T
900     // 3) B = F Z
901     // 4) For each location in locations:
902     //      1) Calculate dA/dt, dS/dt, and dM/dt by substituting 1s into empty matrices
903     //      2) C = B dAdt E F^T
904     //      3) dSigma/dT = C + C^T + B dS/dT B^T
905     //      4) dM/dT = B dA/dT Z b + B dM/dT
906
907     omxRAMExpectation* oro = (omxRAMExpectation*)(oo->argStruct);
908                                 // Size reference: A is axa, F is fxf
909     omxMatrix* A = oro->A;      // axa
910     omxMatrix* S = oro->S;      // axa
911     omxMatrix* F = oro->F;      // fxf
912     omxMatrix* Z = oro->Z;      // axa
913     // omxMatrix* I = oro->I;      // axa
914     omxMatrix* M = oro->M;      // 1xf
915     omxMatrix* B = oro->X;      // fxa
916     omxMatrix* U = oro->U;      // fxa
917     omxMatrix* EF = oro->EF;    // fxa
918     omxMatrix* ZM = oro->ZM;    // 1xf
919     omxMatrix* V = oro->V;      // fxa
920     omxMatrix* W = oro->W;      // axa
921         omxMatrix* E = oro->Ax;     // axa
922     
923     omxMatrix* dA = oro->dA;
924     omxMatrix* dS = oro->dS;
925     omxMatrix* dM = oro->dM;
926     
927     // int numIters = oro->numIters;
928     int Amat = A->matrixNumber;
929     int Smat = S->matrixNumber;
930     int Mmat = 0;
931     if(M != NULL) Mmat = M->matrixNumber;
932     
933     omxFreeVar* varList = oo->currentState->freeVarList;
934     int nLocs = oo->currentState->numFreeParams;
935
936     // Assumed.
937     // if(omxNeedsUpdate(Z)) {
938     //     // (Re)Calculate current A, S, F, and Z
939     //     omxCalculateRAMCovarianceAndMeans(A, S, F, M, cov, means, numIters, I, Z, oro->Y, B, Ax);
940     // }
941     
942     // TODO: Handle repeated-call cases.
943     // E = Z S (Z^T)
944     omxDGEMM(FALSE, FALSE, 1.0, Z, S, 0.0, W);
945     omxDGEMM(FALSE, TRUE, 1.0, W, Z, 0.0, E);
946     
947     //EF = E F^T
948     omxDGEMM(FALSE, TRUE, 1.0, E, F, 0.0, EF);
949
950     // B = F Z
951     omxDGEMM(FALSE, FALSE, 1.0, F, Z, 0.0, B);
952     
953     // ZM = ZM
954     if(M != NULL)
955         omxDGEMV(FALSE, 1.0, Z, M, 0.0, ZM);
956     
957     // Step through free params 
958     // Note: This should be parallelized at the next level up.
959     //  This function itself should serially perform one computation for each location
960     //  given in the sequence.  Always write to the appropriate location so that writes
961     //  can be shared across thread-level parallelism.
962     
963     for(int param = 0; param < nLocs; param++) {
964         //  Repeated from above.  For each parameter:
965         //      1) Calculate dA/dt, dS/dt, and dM/dt by substituting 1s into empty matrices
966         
967         omxFreeVar var = varList[param];
968         
969         // Zero dA, dS, and dM.  // TODO: speed
970         for( int k = 0; k < dA->cols; k++) {
971             for( int j = 0; j < dA->rows; j++) {
972                 omxSetMatrixElement(dA, j, k, 0.0);
973                 omxSetMatrixElement(dS, j, k, 0.0);
974             }
975             if(M != NULL) omxSetMatrixElement(dM, 0, k, 0.0);
976         }
977         status[param] = 0;
978
979         // Create dA, dS, and dM mats for each Free Parameter
980         for(size_t varLoc = 0; varLoc < var.locations.size(); varLoc++) {
981             int varMat = ~var.locations[varLoc].matrix; // Matrices are numbered from ~0 to ~N
982             int row = var.locations[varLoc].row;
983             int col = var.locations[varLoc].col;
984             if(varMat == Amat) {
985                 omxSetMatrixElement(dA, row, col, 1);
986             } else if(varMat == Smat) {
987                 omxSetMatrixElement(dS, row, col, 1);
988             } else if(varMat == Mmat && M != NULL) {
989                 omxSetMatrixElement(dM, row, col, 1);
990             } else {
991                 // This parameter has some outside effect
992                 // We cannot directly estimate its effects on the likelihood
993                 // For now, skip.
994                 // TODO: Find a way to deal with these situations
995                 status[param] = -1;
996                 if(OMX_DEBUG) { mxLog("Skipping parameter %d because %dth element has outside influence %d. Looking for %d, %d, or %d.", param, varLoc, varMat, Amat, Smat, Mmat);}
997                 break;
998             }
999         }
1000         
1001         if(status[param] < 0) {
1002             continue;  // Skip this one.
1003         }
1004         
1005         omxMatrix* C = dSigmas[param];
1006         omxMatrix* dMu = dMus[param];
1007         
1008         //      2) C = B dAdt EF
1009         omxDGEMM(FALSE, FALSE, 1.0, B, dA, 0.0, U);
1010         omxDGEMM(FALSE, FALSE, 1.0, U, EF, 0.0, C);
1011         
1012         // Note: U is saved here for calculation of M, below.
1013         //      3) dSigma/dT = C + C^T + B dS/dT B^T
1014         omxAddOwnTranspose(&C, 0, C);
1015         omxDGEMM(FALSE, FALSE, 1.0, B, dS, 0.0, V);
1016         omxDGEMM(FALSE, TRUE,  1.0, V, B, 1.0, C);
1017         
1018         if(M != NULL) {
1019             //      4) dM/dT = B dA/dT Z M + B dM/dT
1020             omxDGEMV(FALSE, 1.0, U, ZM, 0.0, dMu);
1021             omxDGEMV(FALSE, 1.0, B, dM, 1.0, dMu);
1022
1023         }
1024
1025     }
1026
1027 }
1028
1029 static omxMatrix* omxGetRAMExpectationComponent(omxExpectation* ox, omxFitFunction* off, const char* component) {
1030         
1031         if(OMX_DEBUG) { mxLog("RAM expectation: %s requested--", component); }
1032
1033         omxRAMExpectation* ore = (omxRAMExpectation*)(ox->argStruct);
1034         omxMatrix* retval = NULL;
1035
1036         if(!strncmp("cov", component, 3)) {
1037                 retval = ore->cov;
1038         } else if(!strncmp("mean", component, 4)) {
1039                 retval = ore->means;
1040         } else if(!strncmp("pvec", component, 4)) {
1041                 // Once implemented, change compute function and return pvec
1042         }
1043         
1044         if(OMX_DEBUG) { mxLog("Returning 0x%x.", retval); }
1045
1046         return retval;
1047
1048 }