Disentangle Ramsay1975 caution settings (maybe improves dynamic behavior)
[openmx:openmx.git] / src / Compute.h
1 /*
2  *  Copyright 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 #ifndef _OMX_COMPUTE_H_
18 #define _OMX_COMPUTE_H_
19
20 #define R_NO_REMAP
21 #include <R.h>
22 #include <Rinternals.h>
23 #include "omxDefines.h"
24 #include "Eigen/SparseCore"
25 #include "glue.h"
26
27 // See R/MxRunHelperFunctions.R npsolMessages
28 enum ComputeInform {
29         INFORM_UNINITIALIZED = -1,
30         INFORM_CONVERGED_OPTIMUM = 0,
31         INFORM_UNCONVERGED_OPTIMUM = 1,
32         // The final iterate satisfies the optimality conditions to the accuracy requested,
33         // but the sequence of iterates has not yet converged.
34         // Optimizer was terminated because no further improvement
35         // could be made in the merit function (Mx status GREEN).
36         INFORM_LINEAR_CONSTRAINTS_INFEASIBLE = 2,
37         // The linear constraints and bounds could not be satisfied.
38         // The problem has no feasible solution.
39         INFORM_NONLINEAR_CONSTRAINTS_INFEASIBLE = 3,
40         // The nonlinear constraints and bounds could not be satisfied.
41         // The problem may have no feasible solution.
42         INFORM_ITERATION_LIMIT = 4,
43         // The major iteration limit was reached (Mx status BLUE).
44         INFORM_NOT_AT_OPTIMUM = 6,
45         // The model does not satisfy the first-order optimality conditions
46         // to the required accuracy, and no improved point for the
47         // merit function could be found during the final linesearch (Mx status RED)
48         INFORM_BAD_DERIVATIVES = 7,
49         // The function derivates returned by funcon or funobj
50         // appear to be incorrect.
51         INFORM_INVALID_PARAM = 9
52         // An input parameter was invalid'
53 };
54
55 enum ComputeInfoMethod {
56         INFO_METHOD_DEFAULT,
57         INFO_METHOD_HESSIAN,
58         INFO_METHOD_SANDWICH,
59         INFO_METHOD_BREAD,
60         INFO_METHOD_MEAT
61 };
62
63 struct HessianBlock {
64         std::vector<int> vars;  // global freeVar ID in order
65         Eigen::MatrixXd mat;    // vars * vars, only upper triangle referenced
66
67         HessianBlock() {}
68         HessianBlock *clone();
69         bool posDefinite();
70 };
71
72 // The idea of FitContext is to eventually enable fitting from
73 // multiple starting values in parallel.
74
75 class FitContext {
76         static omxFitFunction *RFitFunction;
77
78         FitContext *parent;
79
80         std::vector<HessianBlock*> allBlocks;
81         //bool overlap;
82
83         bool haveSparseHess;
84         Eigen::SparseMatrix<double> sparseHess;
85         bool haveSparseIHess;
86         Eigen::SparseMatrix<double> sparseIHess;
87
88         bool haveDenseHess;
89         Eigen::MatrixXd hess;
90         bool haveDenseIHess;
91         Eigen::MatrixXd ihess;
92
93  public:
94         FreeVarGroup *varGroup;
95         size_t numParam;               // cached from varGroup
96         std::vector<int> mapToParent;
97         double mac;
98         double fit;
99         double *est;
100         // We need some protocol to manage flavor assignment
101         // when the multigroup fitfunction is involved. TODO
102         int *flavor;
103         Eigen::VectorXd grad;
104         int infoDefinite;
105         double infoCondNum;
106         double *stderrs;   // plural to distinguish from stdio's stderr
107         enum ComputeInfoMethod infoMethod;
108         double *infoA; // sandwich, the bread
109         double *infoB; // sandwich, the meat
110         std::vector<double> caution;
111         int iterations;
112         enum ComputeInform inform;
113         int wanted;
114
115         void init();
116         FitContext(std::vector<double> &startingValues);
117         FitContext(FitContext *parent, FreeVarGroup *group);
118         void allocStderrs();
119         void copyParamToModel(omxState* os, double *at);
120         void copyParamToModel(omxState *os);
121         void copyParamToModel(omxMatrix *mat, double *at);
122         void copyParamToModel(omxMatrix *mat);
123         double *take(int want);
124         void maybeCopyParamToModel(omxState* os);
125         void updateParent();
126         void updateParentAndFree();
127         void log(const char *where);
128         void log(const char *where, int what);
129         ~FitContext();
130         
131         // deriv related
132         void clearHessian();
133         void negateHessian();
134         void queue(HessianBlock *hb);
135         void refreshDenseHess();
136         void refreshDenseIHess();
137         Eigen::VectorXd ihessGradProd();
138         double *getDenseHessUninitialized();
139         double *getDenseIHessUninitialized();
140         double *getDenseHessianish();  // either a Hessian or inverse Hessian, remove TODO
141         void copyDenseHess(double *dest);
142         void copyDenseIHess(double *dest);
143         Eigen::VectorXd ihessDiag();
144         void preInfo();
145         void postInfo();
146
147         static void cacheFreeVarDependencies();
148         static void setRFitFunction(omxFitFunction *rff);
149 };
150
151 typedef std::vector< std::pair<int, MxRList*> > LocalComputeResult;
152
153 class omxCompute {
154         int computeId;
155  protected:
156         virtual void reportResults(FitContext *fc, MxRList *slots, MxRList *glob) {};
157         void collectResultsHelper(FitContext *fc, std::vector< omxCompute* > &clist,
158                                   LocalComputeResult *lcr, MxRList *out);
159         static enum ComputeInfoMethod stringToInfoMethod(const char *iMethod);
160  public:
161         FreeVarGroup *varGroup;
162         omxCompute();
163         virtual void initFromFrontend(SEXP rObj);
164         virtual omxFitFunction *getFitFunction() { return NULL; }
165         void compute(FitContext *fc);
166         virtual void computeImpl(FitContext *fc) {}
167         virtual void collectResults(FitContext *fc, LocalComputeResult *lcr, MxRList *out);
168         virtual double getOptimizerStatus() { return NA_REAL; }  // backward compatibility
169         virtual ~omxCompute();
170 };
171
172 class Ramsay1975 {
173         // Ramsay, J. O. (1975). Solving Implicit Equations in
174         // Psychometric Data Analysis.  Psychometrika, 40(3), 337-360.
175
176         FitContext *fc;
177         size_t numParam;
178         int flavor;
179         int verbose;
180         int boundsHit;
181         double minCaution;
182         double highWatermark;
183         std::vector<int> vars;
184         std::vector<double> prevEst;
185         std::vector<double> prevAdj1;
186         std::vector<double> prevAdj2;
187         bool goingWild;
188
189 public:
190         double maxCaution;
191         double caution;
192
193         Ramsay1975(FitContext *fc, int flavor, double caution, int verbose, double minCaution);
194         void recordEstimate(int px, double newEst);
195         void apply();
196         void recalibrate(bool *restart);
197         void restart(bool myFault);
198 };
199
200 class omxCompute *omxNewCompute(omxState* os, const char *type);
201
202 class omxCompute *newComputeGradientDescent();
203 class omxCompute *newComputeNumericDeriv();
204 class omxCompute *newComputeNewtonRaphson();
205
206 void omxApproxInvertPosDefTriangular(int dim, double *hess, double *ihess, double *stress);
207 void omxApproxInvertPackedPosDefTriangular(int dim, int *mask, double *packedHess, double *stress);
208
209 #endif