Changeset 16251 for branches/2925_AutoDiffForDynamicalModels/HeuristicLab.Problems.DynamicalSystemsModelling/3.3
- Timestamp:
- 10/23/18 19:49:16 (6 years ago)
- Location:
- branches/2925_AutoDiffForDynamicalModels/HeuristicLab.Problems.DynamicalSystemsModelling/3.3
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2925_AutoDiffForDynamicalModels/HeuristicLab.Problems.DynamicalSystemsModelling/3.3/CVODES.cs
r16248 r16251 434 434 public static extern void N_VDestroy_Serial(IntPtr vec); 435 435 436 [DllImport("sundials_cvodes.dll", EntryPoint = "N_VDestroyVectorArray_Serial", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] 437 public static extern void N_VDestroyVectorArray_Serial(IntPtr vecArr, int count); 438 436 439 [DllImport("sundials_cvodes.dll", EntryPoint = "N_VPrint_Serial", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] 437 440 public static extern void N_VPrint_Serial(IntPtr vec); -
branches/2925_AutoDiffForDynamicalModels/HeuristicLab.Problems.DynamicalSystemsModelling/3.3/Problem.cs
r16250 r16251 183 183 Parameters.Add(new FixedValueParameter<BoolValue>(OptimizeParametersForEpisodesParameterName, "Flag to select if parameters should be optimized globally or for each episode individually.", new BoolValue(false))); 184 184 185 var solversStr = new string[] { "HeuristicLab", "C OVDES" };185 var solversStr = new string[] { "HeuristicLab", "CVODES" }; 186 186 var solvers = new ItemSet<StringValue>( 187 187 solversStr.Select(s => new StringValue(s).AsReadOnly()) 188 188 ); 189 Parameters.Add(new ConstrainedValueParameter<StringValue>(OdeSolverParameterName, "The solver to use for solving the initial value ODE problems", solvers ));189 Parameters.Add(new ConstrainedValueParameter<StringValue>(OdeSolverParameterName, "The solver to use for solving the initial value ODE problems", solvers, solvers.First())); 190 190 191 191 RegisterEventHandlers(); … … 195 195 // TODO: UI hangs when selecting / deselecting input variables because the encoding is updated on each item 196 196 // TODO: use training range as default training episode 197 // TODO: write back optimized parameters to solution? 197 198 198 199 } … … 200 201 public override double Evaluate(Individual individual, IRandom random) { 201 202 var trees = individual.Values.Select(v => v.Value).OfType<ISymbolicExpressionTree>().ToArray(); // extract all trees from individual 203 // write back optimized parameters to tree nodes instead of the separate OptTheta variable 204 // retreive optimized parameters from nodes? 202 205 203 206 if (OptimizeParametersForEpisodes) { … … 247 250 } 248 251 249 var nodeIdx = new Dictionary<ISymbolicExpressionTreeNode, int>();250 251 foreach (var t reein trees) {252 foreach (var n ode in tree.Root.IterateNodesPrefix().Where(n => IsConstantNode(n))) {253 nodeIdx.Add(node, nodeIdx.Count);254 }255 }256 257 var theta = nodeIdx.Select(_ => random.NextDouble() * 2.0 - 1.0).ToArray(); // init params randomly from Unif(-1,1)252 // NOTE: the order of values in parameter matches prefix order of constant nodes in trees 253 var paramNodes = new List<ISymbolicExpressionTreeNode>(); 254 foreach (var t in trees) { 255 foreach (var n in t.IterateNodesPrefix()) { 256 if (IsConstantNode(n)) 257 paramNodes.Add(n); 258 } 259 } 260 var theta = paramNodes.Select(_ => random.NextDouble() * 2.0 - 1.0).ToArray(); // init params randomly from Unif(-1,1) 258 261 259 262 optTheta = new double[0]; … … 264 267 alglib.minlbfgssetcond(state, 0.0, 0.0, 0.0, MaximumParameterOptimizationIterations); 265 268 alglib.minlbfgsoptimize(state, EvaluateObjectiveAndGradient, null, 266 new object[] { trees, targetVars, problemData, nodeIdx,targetValues, episodes.ToArray(), NumericIntegrationSteps, latentVariables, OdeSolver }); //TODO: create a type269 new object[] { trees, targetVars, problemData, targetValues, episodes.ToArray(), NumericIntegrationSteps, latentVariables, OdeSolver }); //TODO: create a type 267 270 268 271 alglib.minlbfgsresults(state, out optTheta, out report); … … 301 304 nmse = double.NaN; 302 305 EvaluateObjectiveAndGradient(optTheta, ref nmse, grad, 303 new object[] { trees, targetVars, problemData, nodeIdx, targetValues, episodes.ToArray(), NumericIntegrationSteps, latentVariables});306 new object[] { trees, targetVars, problemData, targetValues, episodes.ToArray(), NumericIntegrationSteps, latentVariables, OdeSolver }); 304 307 if (double.IsNaN(nmse) || double.IsInfinity(nmse)) { nmse = 10E6; return; } // return a large value (TODO: be consistent by using NMSE) 305 308 } … … 309 312 var targetVariables = (string[])((object[])obj)[1]; 310 313 var problemData = (IRegressionProblemData)((object[])obj)[2]; 311 var nodeIdx = (Dictionary<ISymbolicExpressionTreeNode, int>)((object[])obj)[3]; 312 var targetValues = (double[,])((object[])obj)[4]; 313 var episodes = (IntRange[])((object[])obj)[5]; 314 var numericIntegrationSteps = (int)((object[])obj)[6]; 315 var latentVariables = (string[])((object[])obj)[7]; 316 var odeSolver = (string)((object[])obj)[8]; 314 var targetValues = (double[,])((object[])obj)[3]; 315 var episodes = (IntRange[])((object[])obj)[4]; 316 var numericIntegrationSteps = (int)((object[])obj)[5]; 317 var latentVariables = (string[])((object[])obj)[6]; 318 var odeSolver = (string)((object[])obj)[7]; 317 319 318 320 319 321 Tuple<double, Vector>[][] predicted = null; // one array of predictions for each episode 322 // TODO: stop integration early for diverging solutions 320 323 predicted = Integrate( 321 324 trees, // we assume trees contain expressions for the change of each target variable over time y'(t) … … 325 328 latentVariables, 326 329 episodes, 327 nodeIdx,328 330 x, 329 331 odeSolver, 330 332 numericIntegrationSteps).ToArray(); 331 333 334 if (predicted.Length != targetValues.GetLength(0)) { 335 f = double.MaxValue; 336 Array.Clear(grad, 0, grad.Length); 337 return; 338 } 332 339 333 340 // for normalized MSE = 1/variance(t) * MSE(t, pred) … … 378 385 var trees = bestIndividualAndQuality.Item1.Values.Select(v => v.Value).OfType<ISymbolicExpressionTree>().ToArray(); // extract all trees from individual 379 386 380 // TODO extract common functionality from Evaluate and Analyze381 var nodeIdx = new Dictionary<ISymbolicExpressionTreeNode, int>();382 foreach (var tree in trees) {383 foreach (var node in tree.Root.IterateNodesPrefix().Where(n => IsConstantNode(n))) {384 nodeIdx.Add(node, nodeIdx.Count);385 }386 }387 387 var problemData = ProblemData; 388 388 var targetVars = TargetVariables.CheckedItems.Select(i => i.Value).ToArray(); … … 404 404 latentVariables, 405 405 episodes, 406 nodeIdx,407 406 optTheta, 408 407 OdeSolver, … … 440 439 var optTheta = ((DoubleArray)bestIndividualAndQuality.Item1["OptTheta"]).ToArray(); // see evaluate 441 440 var trainingPrediction = Integrate( 442 trees, // we assume trees contain expressions for the change of each target variable over time y'(t)441 trees, // we assume trees contain expressions for the change of each target variable over time dy/dt 443 442 problemData.Dataset, 444 443 problemData.AllowedInputVariables.ToArray(), … … 446 445 latentVariables, 447 446 TrainingEpisodes, 448 nodeIdx,449 447 optTheta, 450 448 OdeSolver, … … 471 469 latentVariables, 472 470 new IntRange[] { ProblemData.TestPartition }, 473 nodeIdx,474 471 optTheta, 475 472 OdeSolver, … … 528 525 } 529 526 530 private ISymbolicExpressionTreeNode TranslateTreeNode(ISymbolicExpressionTreeNode n, double[] parameterValues, ref int nextParIdx) {531 ISymbolicExpressionTreeNode translatedNode = null;532 if (n.Symbol is StartSymbol) {533 translatedNode = new StartSymbol().CreateTreeNode();534 } else if (n.Symbol is ProgramRootSymbol) {535 translatedNode = new ProgramRootSymbol().CreateTreeNode();536 } else if (n.Symbol.Name == "+") {537 translatedNode = new Addition().CreateTreeNode();538 } else if (n.Symbol.Name == "-") {539 translatedNode = new Subtraction().CreateTreeNode();540 } else if (n.Symbol.Name == "*") {541 translatedNode = new Multiplication().CreateTreeNode();542 } else if (n.Symbol.Name == "%") {543 translatedNode = new Division().CreateTreeNode();544 } else if (n.Symbol.Name == "sin") {545 translatedNode = new Sine().CreateTreeNode();546 } else if (n.Symbol.Name == "cos") {547 translatedNode = new Cosine().CreateTreeNode();548 } else if (IsConstantNode(n)) {549 var constNode = (ConstantTreeNode)new Constant().CreateTreeNode();550 constNode.Value = parameterValues[nextParIdx];551 nextParIdx++;552 translatedNode = constNode;553 } else {554 // assume a variable name555 var varName = n.Symbol.Name;556 var varNode = (VariableTreeNode)new Variable().CreateTreeNode();557 varNode.Weight = 1.0;558 varNode.VariableName = varName;559 translatedNode = varNode;560 }561 foreach (var child in n.Subtrees) {562 translatedNode.AddSubtree(TranslateTreeNode(child, parameterValues, ref nextParIdx));563 }564 return translatedNode;565 }566 527 567 528 #region interpretation … … 607 568 private static IEnumerable<Tuple<double, Vector>[]> Integrate( 608 569 ISymbolicExpressionTree[] trees, IDataset dataset, string[] inputVariables, string[] targetVariables, string[] latentVariables, IEnumerable<IntRange> episodes, 609 Dictionary<ISymbolicExpressionTreeNode, int> nodeIdx,double[] parameterValues,570 double[] parameterValues, 610 571 string odeSolver, int numericIntegrationSteps = 100) { 611 572 … … 631 592 // add value entries for latent variables which are also integrated 632 593 foreach (var latentVar in latentVariables) { 633 variableValues.Add(latentVar, Tuple.Create(0.0, Vector.Zero)); // we don't have observations for latent variables -> assume zero as starting value 594 variableValues.Add(latentVar, Tuple.Create(0.0, Vector.Zero)); // we don't have observations for latent variables -> assume zero as starting value TODO 634 595 } 635 596 var calculatedVariables = targetVariables.Concat(latentVariables).ToArray(); // TODO: must conincide with the order of trees in the encoding … … 637 598 foreach (var t in rows.Skip(1)) { 638 599 if (odeSolver == "HeuristicLab") 639 IntegrateHL(trees, nodeIdx,calculatedVariables, variableValues, parameterValues, numericIntegrationSteps);600 IntegrateHL(trees, calculatedVariables, variableValues, parameterValues, numericIntegrationSteps); 640 601 else if (odeSolver == "CVODES") 641 IntegrateCVODES(trees, nodeIdx, calculatedVariables, variableValues, parameterValues);602 IntegrateCVODES(trees, calculatedVariables, variableValues, parameterValues, t); 642 603 else throw new InvalidOperationException("Unknown ODE solver " + odeSolver); 643 604 644 // only return the target variables for calculation of errors 645 yield return targetVariables 646 .Select(targetVar => variableValues[targetVar]) 647 .ToArray(); 605 if (variableValues.Count == targetVariables.Length) { 606 // only return the target variables for calculation of errors 607 var res = targetVariables 608 .Select(targetVar => variableValues[targetVar]) 609 .ToArray(); 610 if (res.Any(ri => double.IsNaN(ri.Item1) || double.IsInfinity(ri.Item1))) yield break; 611 yield return res; 612 } else { 613 yield break; // stop early on problems 614 } 615 648 616 649 617 // update for next time step … … 656 624 657 625 private static void IntegrateCVODES( 658 ISymbolicExpressionTree[] trees, // f( u,p) in tree representation659 Dictionary<ISymbolicExpressionTreeNode, int> nodeIdx,660 string[] calculatedVariables, // names of elements of u661 Dictionary<string, Tuple<double, Vector>> variableValues, // u(t): output of the system662 double [] parameterValues // p626 ISymbolicExpressionTree[] trees, // f(y,p) in tree representation 627 string[] calculatedVariables, // names of elements of y 628 Dictionary<string, Tuple<double, Vector>> variableValues, // y (intput and output) input: y(t0), output: y(t0+t) 629 double[] parameterValues, // p 630 double t // duration t for which we want to integrate 663 631 ) { 632 664 633 // the RHS of the ODE 665 CVODES.CVRhsFunc f = CreateOdeRhs(trees, nodeIdx, calculatedVariables, parameterValues); 666 // XXX Jacobian function 667 } 634 // dy/dt = f(y_t,x_t,p) 635 CVODES.CVRhsFunc f = CreateOdeRhs(trees, calculatedVariables, parameterValues); 636 // the Jacobian ∂f/∂y 637 CVODES.CVDlsJacFunc jac = CreateJac(trees, calculatedVariables, parameterValues); 638 639 // the RHS for the forward sensitivities (∂f/∂y)s_i(t) + ∂f/∂p_i 640 CVODES.CVSensRhsFn sensF = CreateSensitivityRhs(trees, calculatedVariables, parameterValues); 641 642 // setup solver 643 int numberOfEquations = trees.Length; 644 IntPtr y = IntPtr.Zero; 645 IntPtr cvode_mem = IntPtr.Zero; 646 IntPtr A = IntPtr.Zero; 647 IntPtr yS0 = IntPtr.Zero; 648 IntPtr linearSolver = IntPtr.Zero; 649 var ns = parameterValues.Length; // number of parameters 650 651 try { 652 y = CVODES.N_VNew_Serial(numberOfEquations); 653 // init y to current values of variables 654 // y must be initialized before calling CVodeInit 655 for (int i = 0; i < calculatedVariables.Length; i++) { 656 CVODES.NV_Set_Ith_S(y, i, variableValues[calculatedVariables[i]].Item1); 657 } 658 659 cvode_mem = CVODES.CVodeCreate(CVODES.MultistepMethod.CV_ADAMS, CVODES.NonlinearSolverIteration.CV_FUNCTIONAL); 660 661 var flag = CVODES.CVodeInit(cvode_mem, f, 0.0, y); 662 Debug.Assert(CVODES.CV_SUCCESS == flag); 663 664 double relTol = 1.0e-2; 665 double absTol = 1.0; 666 flag = CVODES.CVodeSStolerances(cvode_mem, relTol, absTol); // TODO: probably need to adjust absTol per variable 667 Debug.Assert(CVODES.CV_SUCCESS == flag); 668 669 A = CVODES.SUNDenseMatrix(numberOfEquations, numberOfEquations); 670 Debug.Assert(A != IntPtr.Zero); 671 672 linearSolver = CVODES.SUNDenseLinearSolver(y, A); 673 Debug.Assert(linearSolver != IntPtr.Zero); 674 675 flag = CVODES.CVDlsSetLinearSolver(cvode_mem, linearSolver, A); 676 Debug.Assert(CVODES.CV_SUCCESS == flag); 677 678 flag = CVODES.CVDlsSetJacFn(cvode_mem, jac); 679 Debug.Assert(CVODES.CV_SUCCESS == flag); 680 681 yS0 = CVODES.N_VCloneVectorArray_Serial(ns, y); // clone the output vector for each parameter 682 unsafe { 683 // set to initial sensitivities supplied by caller 684 for (int pIdx = 0; pIdx < ns; pIdx++) { 685 var yS0_i = *((IntPtr*)yS0.ToPointer() + pIdx); 686 for (var varIdx = 0; varIdx < calculatedVariables.Length; varIdx++) { 687 CVODES.NV_Set_Ith_S(yS0_i, varIdx, variableValues[calculatedVariables[varIdx]].Item2[pIdx]); 688 } 689 } 690 } 691 692 flag = CVODES.CVodeSensInit(cvode_mem, ns, CVODES.CV_SIMULTANEOUS, sensF, yS0); 693 Debug.Assert(CVODES.CV_SUCCESS == flag); 694 695 flag = CVODES.CVodeSensEEtolerances(cvode_mem); 696 Debug.Assert(CVODES.CV_SUCCESS == flag); 697 698 // make one forward integration step 699 double tout = 0.0; // first output time 700 flag = CVODES.CVode(cvode_mem, t, y, ref tout, CVODES.CV_NORMAL); 701 if (flag == CVODES.CV_SUCCESS) { 702 Debug.Assert(t == tout); 703 704 // get sensitivities 705 flag = CVODES.CVodeGetSens(cvode_mem, ref tout, yS0); 706 Debug.Assert(CVODES.CV_SUCCESS == flag); 707 708 // update variableValues based on integration results 709 for (int varIdx = 0; varIdx < calculatedVariables.Length; varIdx++) { 710 var yi = CVODES.NV_Get_Ith_S(y, varIdx); 711 var gArr = new double[parameterValues.Length]; 712 for (var pIdx = 0; pIdx < parameterValues.Length; pIdx++) { 713 unsafe { 714 var yS0_pi = *((IntPtr*)yS0.ToPointer() + pIdx); 715 gArr[pIdx] = CVODES.NV_Get_Ith_S(yS0_pi, varIdx); 716 } 717 } 718 variableValues[calculatedVariables[varIdx]] = Tuple.Create(yi, new Vector(gArr)); 719 } 720 } else { 721 variableValues.Clear(); // indicate problems by not returning new values 722 } 723 724 // cleanup all allocated objects 725 } finally { 726 if (y != IntPtr.Zero) CVODES.N_VDestroy_Serial(y); 727 if (cvode_mem != IntPtr.Zero) CVODES.CVodeFree(cvode_mem); 728 if (linearSolver != IntPtr.Zero) CVODES.SUNLinSolFree(linearSolver); 729 if (A != IntPtr.Zero) CVODES.SUNMatDestroy(A); 730 if (yS0 != IntPtr.Zero) CVODES.N_VDestroyVectorArray_Serial(yS0, ns); 731 } 732 } 733 668 734 669 735 private static CVODES.CVRhsFunc CreateOdeRhs( 670 736 ISymbolicExpressionTree[] trees, 671 Dictionary<ISymbolicExpressionTreeNode, int> nodeIdx,672 737 string[] calculatedVariables, 673 738 double[] parameterValues) { 674 var variableValues = new Dictionary<string, Tuple<double, Vector>>(); 675 // init variableValues with zeros 676 for (int i = 0; i < calculatedVariables.Length; i++) { 677 var backingArr = new double[parameterValues.Length]; 678 variableValues.Add(calculatedVariables[i], Tuple.Create(0.0, new Vector(backingArr))); 679 } 739 // we don't need to calculate a gradient here -> no nodes are selected for 740 // --> no nodes are selected to be included in the gradient calculation 741 var nodeIdx = new Dictionary<ISymbolicExpressionTreeNode, int>(); 680 742 return (double t, 681 743 IntPtr y, // N_Vector, current value of y (input) … … 683 745 IntPtr user_data // optional user data, (unused here) 684 746 ) => { 685 // copy y array to variableValues dictionary 686 for(int i=0;i<calculatedVariables.Length;i++) { 687 var yi = CVODES.NV_Get_Ith_S(y, (long)i); 688 variableValues[calculatedVariables[i]] = Tuple.Create(yi, Vector.Zero); 747 // TODO: perf 748 var nodeValues = new Dictionary<ISymbolicExpressionTreeNode, Tuple<double, Vector>>(); 749 750 int pIdx = 0; 751 foreach (var tree in trees) { 752 foreach (var n in tree.IterateNodesPrefix()) { 753 if (IsConstantNode(n)) { 754 nodeValues.Add(n, Tuple.Create(parameterValues[pIdx], Vector.Zero)); // here we do not need a gradient 755 pIdx++; 756 } else if (n.SubtreeCount == 0) { 757 // for variables and latent variables get the value from variableValues 758 var varName = n.Symbol.Name; 759 var varIdx = Array.IndexOf(calculatedVariables, varName); // TODO: perf! 760 var y_i = CVODES.NV_Get_Ith_S(y, (long)varIdx); 761 nodeValues.Add(n, Tuple.Create(y_i, Vector.Zero)); // no gradient needed 762 } 763 } 689 764 } 690 765 for (int i = 0; i < trees.Length; i++) { 691 766 var tree = trees[i]; 692 var res_i = InterpretRec(tree.Root , variableValues, nodeIdx, parameterValues); // TODO: perf - we don't need AutoDiff-based gradients here767 var res_i = InterpretRec(tree.Root.GetSubtree(0).GetSubtree(0), nodeValues); 693 768 CVODES.NV_Set_Ith_S(ydot, i, res_i.Item1); 694 769 } … … 697 772 } 698 773 699 private static void IntegrateHL(ISymbolicExpressionTree[] trees, Dictionary<ISymbolicExpressionTreeNode, int> nodeIdx, 774 private static CVODES.CVDlsJacFunc CreateJac( 775 ISymbolicExpressionTree[] trees, 700 776 string[] calculatedVariables, 701 Dictionary<string, Tuple<double, Vector>> variableValues, 777 double[] parameterValues) { 778 779 return ( 780 double t, // current time (input) 781 IntPtr y, // N_Vector, current value of y (input) 782 IntPtr fy, // N_Vector, current value of f (input) 783 IntPtr Jac, // SUNMatrix ∂f/∂y (output, rows i contains are ∂f_i/∂y vector) 784 IntPtr user_data, // optional (unused here) 785 IntPtr tmp1, // N_Vector, optional (unused here) 786 IntPtr tmp2, // N_Vector, optional (unused here) 787 IntPtr tmp3 // N_Vector, optional (unused here) 788 ) => { 789 // here we need to calculate partial derivatives for the calculated variables y 790 var nodeValues = new Dictionary<ISymbolicExpressionTreeNode, Tuple<double, Vector>>(); 791 int pIdx = 0; 792 foreach (var tree in trees) { 793 foreach (var n in tree.IterateNodesPrefix()) { 794 if (IsConstantNode(n)) { 795 nodeValues.Add(n, Tuple.Create(parameterValues[pIdx], Vector.Zero)); // here we need a gradient over y which is zero for parameters 796 pIdx++; 797 } else if (n.SubtreeCount == 0) { 798 // for variables and latent variables we use values supplied in y and init gradient vectors accordingly 799 var varName = n.Symbol.Name; 800 var varIdx = Array.IndexOf(calculatedVariables, varName); // TODO: perf! 801 var y_i = CVODES.NV_Get_Ith_S(y, (long)varIdx); 802 var gArr = new double[CVODES.NV_LENGTH_S(y)]; // backing array 803 gArr[varIdx] = 1.0; 804 var g = new Vector(gArr); 805 nodeValues.Add(n, Tuple.Create(y_i, g)); 806 } 807 } 808 } 809 810 for (int i = 0; i < trees.Length; i++) { 811 var tree = trees[i]; 812 var res = InterpretRec(tree.Root.GetSubtree(0).GetSubtree(0), nodeValues); 813 var g = res.Item2; 814 for (int j = 0; j < calculatedVariables.Length; j++) { 815 CVODES.SUNDenseMatrix_Set(Jac, i, j, g[j]); 816 } 817 } 818 return 0; // on success 819 }; 820 } 821 822 823 // to calculate sensitivities RHS for all equations at once 824 // must compute (∂f/∂y)s_i(t) + ∂f/∂p_i and store in ySdot. 825 // Index i refers to parameters, dimensionality of matrix and vectors is number of equations 826 private static CVODES.CVSensRhsFn CreateSensitivityRhs(ISymbolicExpressionTree[] trees, string[] calculatedVariables, double[] parameterValues) { 827 return ( 828 int Ns, // number of parameters 829 double t, // current time 830 IntPtr y, // N_Vector y(t) (input) 831 IntPtr ydot, // N_Vector dy/dt(t) (input) 832 IntPtr yS, // N_Vector*, one vector for each parameter (input) 833 IntPtr ySdot, // N_Vector*, one vector for each parameter (output) 834 IntPtr user_data, // optional (unused here) 835 IntPtr tmp1, // N_Vector, optional (unused here) 836 IntPtr tmp2 // N_Vector, optional (unused here) 837 ) => { 838 // here we need to calculate partial derivatives for the calculated variables y as well as for the parameters 839 var nodeValues = new Dictionary<ISymbolicExpressionTreeNode, Tuple<double, Vector>>(); 840 var d = calculatedVariables.Length + parameterValues.Length; // dimensionality of gradient 841 // first collect variable values 842 foreach (var tree in trees) { 843 foreach (var n in tree.IterateNodesPrefix()) { 844 if (IsVariableNode(n)) { 845 // for variables and latent variables we use values supplied in y and init gradient vectors accordingly 846 var varName = n.Symbol.Name; 847 var varIdx = Array.IndexOf(calculatedVariables, varName); // TODO: perf! 848 var y_i = CVODES.NV_Get_Ith_S(y, (long)varIdx); 849 var gArr = new double[d]; // backing array 850 gArr[varIdx] = 1.0; 851 var g = new Vector(gArr); 852 nodeValues.Add(n, Tuple.Create(y_i, g)); 853 } 854 } 855 } 856 // then collect constants 857 int pIdx = 0; 858 foreach (var tree in trees) { 859 foreach (var n in tree.IterateNodesPrefix()) { 860 if (IsConstantNode(n)) { 861 var gArr = new double[d]; 862 gArr[calculatedVariables.Length + pIdx] = 1.0; 863 var g = new Vector(gArr); 864 nodeValues.Add(n, Tuple.Create(parameterValues[pIdx], g)); 865 pIdx++; 866 } 867 } 868 } 869 // gradient vector is [∂f/∂y_1, ∂f/∂y_2, ... ∂f/∂yN, ∂f/∂p_1 ... ∂f/∂p_K] 870 871 872 for (pIdx = 0; pIdx < Ns; pIdx++) { 873 unsafe { 874 var sDot_pi = *((IntPtr*)ySdot.ToPointer() + pIdx); 875 CVODES.N_VConst_Serial(0.0, sDot_pi); 876 } 877 } 878 879 for (int i = 0; i < trees.Length; i++) { 880 var tree = trees[i]; 881 var res = InterpretRec(tree.Root.GetSubtree(0).GetSubtree(0), nodeValues); 882 var g = res.Item2; 883 884 885 // update ySdot = (∂f/∂y)s_i(t) + ∂f/∂p_i 886 887 for (pIdx = 0; pIdx < Ns; pIdx++) { 888 unsafe { 889 var sDot_pi = *((IntPtr*)ySdot.ToPointer() + pIdx); 890 var s_pi = *((IntPtr*)yS.ToPointer() + pIdx); 891 892 var v = CVODES.NV_Get_Ith_S(sDot_pi, i); 893 // (∂f/∂y)s_i(t) 894 var p = 0.0; 895 for (int yIdx = 0; yIdx < calculatedVariables.Length; yIdx++) { 896 p += g[yIdx] * CVODES.NV_Get_Ith_S(s_pi, yIdx); 897 } 898 // + ∂f/∂p_i 899 CVODES.NV_Set_Ith_S(sDot_pi, i, v + p + g[calculatedVariables.Length + pIdx]); 900 } 901 } 902 903 } 904 return 0; // on success 905 }; 906 } 907 908 private static void IntegrateHL( 909 ISymbolicExpressionTree[] trees, 910 string[] calculatedVariables, // names of integrated variables 911 Dictionary<string, Tuple<double, Vector>> variableValues, //y (intput and output) input: y(t0), output: y(t0+1) 702 912 double[] parameterValues, 703 913 int numericIntegrationSteps) { 914 915 var nodeValues = new Dictionary<ISymbolicExpressionTreeNode, Tuple<double, Vector>>(); 916 917 // the nodeValues for parameters are constant over time 918 // TODO: this needs to be done only once for each iteration in gradient descent (whenever parameter values change) 919 // NOTE: the order must be the same as above (prefix order for nodes) 920 int pIdx = 0; 921 foreach (var tree in trees) { 922 foreach (var node in tree.Root.IterateNodesPrefix()) { 923 if (IsConstantNode(node)) { 924 var gArr = new double[parameterValues.Length]; // backing array 925 gArr[pIdx] = 1.0; 926 var g = new Vector(gArr); 927 nodeValues.Add(node, new Tuple<double, Vector>(parameterValues[pIdx], g)); 928 pIdx++; 929 } else if (node.SubtreeCount == 0) { 930 // for (latent) variables get the values from variableValues 931 var varName = node.Symbol.Name; 932 nodeValues.Add(node, variableValues[varName]); 933 } 934 } 935 } 936 937 704 938 double h = 1.0 / numericIntegrationSteps; 705 939 for (int step = 0; step < numericIntegrationSteps; step++) { 706 940 var deltaValues = new Dictionary<string, Tuple<double, Vector>>(); 707 foreach (var tup in trees.Zip(calculatedVariables, Tuple.Create)) { 708 var tree = tup.Item1; 709 var targetVarName = tup.Item2; 710 // skip programRoot and startSymbol 711 var res = InterpretRec(tree.Root.GetSubtree(0).GetSubtree(0), variableValues, nodeIdx, parameterValues); 941 for (int i = 0; i < trees.Length; i++) { 942 var tree = trees[i]; 943 var targetVarName = calculatedVariables[i]; 944 945 // Root.GetSubtree(0).GetSubtree(0) skips programRoot and startSymbol 946 var res = InterpretRec(tree.Root.GetSubtree(0).GetSubtree(0), nodeValues); 712 947 deltaValues.Add(targetVarName, res); 713 948 } 714 949 715 // update variableValues for next step, trape coid integration950 // update variableValues for next step, trapezoid integration 716 951 foreach (var kvp in deltaValues) { 717 952 var oldVal = variableValues[kvp.Key]; 718 var iableValues[kvp.Key]= Tuple.Create(953 var newVal = Tuple.Create( 719 954 oldVal.Item1 + h * kvp.Value.Item1, 720 955 oldVal.Item2 + h * kvp.Value.Item2 721 956 ); 957 variableValues[kvp.Key] = newVal; 958 } 959 // update nodeValues from variableValues 960 // TODO: perf using dictionary with list of nodes for each variable 961 foreach (var tree in trees) { 962 foreach (var node in tree.Root.IterateNodesPrefix().Where(n => IsVariableNode(n))) { 963 var varName = node.Symbol.Name; 964 nodeValues[node] = variableValues[varName]; 965 } 722 966 } 723 967 } … … 726 970 private static Tuple<double, Vector> InterpretRec( 727 971 ISymbolicExpressionTreeNode node, 728 Dictionary<string, Tuple<double, Vector>> variableValues, 729 Dictionary<ISymbolicExpressionTreeNode, int> nodeIdx, 730 double[] parameterValues 972 Dictionary<ISymbolicExpressionTreeNode, Tuple<double, Vector>> nodeValues // contains value and gradient vector for a node (variables and constants only) 731 973 ) { 732 974 733 975 switch (node.Symbol.Name) { 734 976 case "+": { 735 var l = InterpretRec(node.GetSubtree(0), variableValues, nodeIdx, parameterValues); // TODO capture all parameters into a state type for interpretation736 var r = InterpretRec(node.GetSubtree(1), variableValues, nodeIdx, parameterValues);977 var l = InterpretRec(node.GetSubtree(0), nodeValues); 978 var r = InterpretRec(node.GetSubtree(1), nodeValues); 737 979 738 980 return Tuple.Create(l.Item1 + r.Item1, l.Item2 + r.Item2); 739 981 } 740 982 case "*": { 741 var l = InterpretRec(node.GetSubtree(0), variableValues, nodeIdx, parameterValues);742 var r = InterpretRec(node.GetSubtree(1), variableValues, nodeIdx, parameterValues);983 var l = InterpretRec(node.GetSubtree(0), nodeValues); 984 var r = InterpretRec(node.GetSubtree(1), nodeValues); 743 985 744 986 return Tuple.Create(l.Item1 * r.Item1, l.Item2 * r.Item1 + l.Item1 * r.Item2); … … 746 988 747 989 case "-": { 748 var l = InterpretRec(node.GetSubtree(0), variableValues, nodeIdx, parameterValues);749 var r = InterpretRec(node.GetSubtree(1), variableValues, nodeIdx, parameterValues);990 var l = InterpretRec(node.GetSubtree(0), nodeValues); 991 var r = InterpretRec(node.GetSubtree(1), nodeValues); 750 992 751 993 return Tuple.Create(l.Item1 - r.Item1, l.Item2 - r.Item2); 752 994 } 753 995 case "%": { 754 var l = InterpretRec(node.GetSubtree(0), variableValues, nodeIdx, parameterValues);755 var r = InterpretRec(node.GetSubtree(1), variableValues, nodeIdx, parameterValues);996 var l = InterpretRec(node.GetSubtree(0), nodeValues); 997 var r = InterpretRec(node.GetSubtree(1), nodeValues); 756 998 757 999 // protected division … … 766 1008 } 767 1009 case "sin": { 768 var x = InterpretRec(node.GetSubtree(0), variableValues, nodeIdx, parameterValues);1010 var x = InterpretRec(node.GetSubtree(0), nodeValues); 769 1011 return Tuple.Create( 770 1012 Math.Sin(x.Item1), … … 773 1015 } 774 1016 case "cos": { 775 var x = InterpretRec(node.GetSubtree(0), variableValues, nodeIdx, parameterValues);1017 var x = InterpretRec(node.GetSubtree(0), nodeValues); 776 1018 return Tuple.Create( 777 1019 Math.Cos(x.Item1), … … 780 1022 } 781 1023 default: { 782 // distinguish other cases 783 if (IsConstantNode(node)) { 784 var vArr = new double[parameterValues.Length]; // backing array for vector 785 vArr[nodeIdx[node]] = 1.0; 786 var g = new Vector(vArr); 787 return Tuple.Create(parameterValues[nodeIdx[node]], g); 788 } else { 789 // assume a variable name 790 var varName = node.Symbol.Name; 791 return variableValues[varName]; 792 } 1024 return nodeValues[node]; // value and gradient for constants and variables must be set by the caller 793 1025 } 794 1026 } … … 892 1124 return n.Symbol.Name.StartsWith("λ"); 893 1125 } 1126 private static bool IsVariableNode(ISymbolicExpressionTreeNode n) { 1127 return (n.SubtreeCount == 0) && !IsConstantNode(n) && !IsLatentVariableNode(n); 1128 } 894 1129 895 1130 … … 947 1182 } 948 1183 1184 1185 private ISymbolicExpressionTreeNode TranslateTreeNode(ISymbolicExpressionTreeNode n, double[] parameterValues, ref int nextParIdx) { 1186 ISymbolicExpressionTreeNode translatedNode = null; 1187 if (n.Symbol is StartSymbol) { 1188 translatedNode = new StartSymbol().CreateTreeNode(); 1189 } else if (n.Symbol is ProgramRootSymbol) { 1190 translatedNode = new ProgramRootSymbol().CreateTreeNode(); 1191 } else if (n.Symbol.Name == "+") { 1192 translatedNode = new Addition().CreateTreeNode(); 1193 } else if (n.Symbol.Name == "-") { 1194 translatedNode = new Subtraction().CreateTreeNode(); 1195 } else if (n.Symbol.Name == "*") { 1196 translatedNode = new Multiplication().CreateTreeNode(); 1197 } else if (n.Symbol.Name == "%") { 1198 translatedNode = new Division().CreateTreeNode(); 1199 } else if (n.Symbol.Name == "sin") { 1200 translatedNode = new Sine().CreateTreeNode(); 1201 } else if (n.Symbol.Name == "cos") { 1202 translatedNode = new Cosine().CreateTreeNode(); 1203 } else if (IsConstantNode(n)) { 1204 var constNode = (ConstantTreeNode)new Constant().CreateTreeNode(); 1205 constNode.Value = parameterValues[nextParIdx]; 1206 nextParIdx++; 1207 translatedNode = constNode; 1208 } else { 1209 // assume a variable name 1210 var varName = n.Symbol.Name; 1211 var varNode = (VariableTreeNode)new Variable().CreateTreeNode(); 1212 varNode.Weight = 1.0; 1213 varNode.VariableName = varName; 1214 translatedNode = varNode; 1215 } 1216 foreach (var child in n.Subtrees) { 1217 translatedNode.AddSubtree(TranslateTreeNode(child, parameterValues, ref nextParIdx)); 1218 } 1219 return translatedNode; 1220 } 949 1221 #endregion 950 1222 -
branches/2925_AutoDiffForDynamicalModels/HeuristicLab.Problems.DynamicalSystemsModelling/3.3/Vector.cs
r16249 r16251 78 78 } 79 79 80 public double this[int idx] { 81 get { 82 if (this == Vector.Zero) return 0.0; 83 else return arr[idx]; 84 } 85 set { 86 if (this == Vector.Zero) throw new InvalidOperationException(); 87 else arr[idx] = value; 88 } 89 } 90 91 public int Length { 92 get { 93 if (this == Vector.Zero) throw new InvalidOperationException(); 94 else return arr.Length; 95 } 96 } 97 80 98 private readonly double[] arr; // backing array; 81 99
Note: See TracChangeset
for help on using the changeset viewer.