Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2929_PrioritizedGrammarEnumeration/HeuristicLab.Algorithms.DataAnalysis.PGE/3.3/PGE.cs @ 16316

Last change on this file since 16316 was 16316, checked in by gkronber, 5 years ago

#2929: fixed program crash (searchVar must be zero!)

File size: 22.2 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Runtime.InteropServices;
5using System.Threading;
6using HeuristicLab.Analysis;
7using HeuristicLab.Common;
8using HeuristicLab.Core;
9using HeuristicLab.Data;
10using System.Text.RegularExpressions;
11using HeuristicLab.Optimization;
12using HeuristicLab.Parameters;
13using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
14using HeuristicLab.Problems.DataAnalysis;
15using HeuristicLab.Problems.DataAnalysis.Symbolic;
16using HeuristicLab.Problems.DataAnalysis.Symbolic.Regression;
17
18namespace PGE {
19  [Item(Name = "Priorizied Grammar Enumeration (PGE)", Description = "Priorizied grammar enumeration algorithm. Worm, T. and Chiu K., 'Prioritized Grammar Enumeration: Symbolic Regression by Dynamic Programming'. GECCO 2013")]
20
21  [Creatable(Category = CreatableAttribute.Categories.Algorithms, Priority = 999)]
22
23  [StorableClass]
24  public unsafe class PGE : BasicAlgorithm {
25
26    [DllImport("go-pge.dll", EntryPoint = "addTestData", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
27    public static extern void AddTestData(IntPtr indepNames, IntPtr depndNames, IntPtr matrix, int nEntries);
28
29    [DllImport("go-pge.dll", EntryPoint = "addTrainData", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
30    public static extern void AddTrainData(IntPtr indepNames, IntPtr depndNames, IntPtr matrix, int nEntries);
31
32    [DllImport("go-pge.dll", EntryPoint = "initSearch", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
33    public static extern void InitSearch(int maxGen, int pgeRptEpoch, int pgeRptCount, int pgeArchiveCap, int peelCnt, int evalrCount, double zeroEpsilon, IntPtr initMethod, IntPtr growMethod, int sortType);
34
35    [DllImport("go-pge.dll", EntryPoint = "initTreeParams", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
36    public static extern void InitTreeParams(IntPtr roots, IntPtr nodes, IntPtr nonTrig, IntPtr leafs, IntPtr usableVars, int numUsableVars, int maxSize, int minSize, int maxDepth, int minDepth);
37
38    [DllImport("go-pge.dll", EntryPoint = "initProblem", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
39    public static extern void InitProblem(IntPtr name, int maxIter, double hitRatio, int searchVar, IntPtr ProblemTypeString, int numProcs);
40
41    [DllImport("go-pge.dll", EntryPoint = "stepW", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
42    public static extern int StepW();
43
44    [DllImport("go-pge.dll", EntryPoint = "getStepResult", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
45    public static extern IntPtr GetStepResult(out int noBestPush, out int bestNewMinErr, out int bestlen1, out int bestlen2, out int testscore, out int nCoeff);
46
47    [DllImport("go-pge.dll", EntryPoint = "getCoeffResult", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
48    public static extern double GetCoeffResult();
49
50    public override Type ProblemType { get { return typeof(RegressionProblem); } }
51    public new RegressionProblem Problem { get { return (RegressionProblem)base.Problem; } }
52
53    #region parameter names
54    private static readonly string MaxIterationsParameterName = "MaxIterations";
55    private static readonly string MaxGenParameterName = "MaxGen";
56    private static readonly string EvalrCountParameterName = "EvalrCount";
57    private static readonly string MaxSizeParameterName = "MaxSize";
58    private static readonly string MinSizeParameterName = "MinSize";
59    private static readonly string MaxDepthParameterName = "MaxDepth";
60    private static readonly string MinDepthParameterName = "MinDepth";
61    private static readonly string PgeRptEpochParameterName = "PgeRptEpoch";
62    private static readonly string PgeRptCountParameterName = "PgeRptCount";
63    private static readonly string PgeArchiveCapParameterName = "PgeArchiveCap";
64    private static readonly string PeelCntParameterName = "PeelCnt";
65    private static readonly string ZeroEpsilonParameterName = "ZeroEpsilon";
66    private static readonly string HitRatioParameterName = "HitRatio";
67    private static readonly string InitMethodParameterName = "InitMethod";
68    private static readonly string GrowMethodParameterName = "GrowMethod";
69    private static readonly string RootsParameterName = "Roots";
70    private static readonly string NodesParameterName = "Nodes";
71    private static readonly string NonTrigParameterName = "NonTrig";
72    private static readonly string LeafsParameterName = "Leafs";
73
74    #endregion
75
76    #region parameters                                           
77    private IFixedValueParameter<IntValue> MaxIterationsParameter {
78      get { return (IFixedValueParameter<IntValue>)Parameters[MaxIterationsParameterName]; }
79    }
80    public int MaxIterations {
81      get { return MaxIterationsParameter.Value.Value; }
82      set { MaxIterationsParameter.Value.Value = value; }
83    }
84
85    private IFixedValueParameter<IntValue> MaxGenParameter {
86      get { return (IFixedValueParameter<IntValue>)Parameters[MaxGenParameterName]; }
87    }
88    public int MaxGen {
89      get { return MaxGenParameter.Value.Value; }
90      set { MaxGenParameter.Value.Value = value; }
91    }
92
93    private IFixedValueParameter<IntValue> EvalrCountParameter {
94      get { return (IFixedValueParameter<IntValue>)Parameters[EvalrCountParameterName]; }
95    }
96    public int EvalrCount {
97      get { return EvalrCountParameter.Value.Value; }
98      set { EvalrCountParameter.Value.Value = value; }
99    }
100
101    private IFixedValueParameter<IntValue> MaxSizeParameter {
102      get { return (IFixedValueParameter<IntValue>)Parameters[MaxSizeParameterName]; }
103    }
104    public int MaxSize {
105      get { return MaxSizeParameter.Value.Value; }
106      set { MaxSizeParameter.Value.Value = value; }
107    }
108
109    private IFixedValueParameter<IntValue> MinSizeParameter {
110      get { return (IFixedValueParameter<IntValue>)Parameters[MinSizeParameterName]; }
111    }
112    public int MinSize {
113      get { return MinSizeParameter.Value.Value; }
114      set { MinSizeParameter.Value.Value = value; }
115    }
116
117    private IFixedValueParameter<IntValue> MaxDepthParameter {
118      get { return (IFixedValueParameter<IntValue>)Parameters[MaxDepthParameterName]; }
119    }
120    public int MaxDepth {
121      get { return MaxDepthParameter.Value.Value; }
122      set { MaxDepthParameter.Value.Value = value; }
123    }
124
125    private IFixedValueParameter<IntValue> MinDepthParameter {
126      get { return (IFixedValueParameter<IntValue>)Parameters[MinDepthParameterName]; }
127    }
128    public int MinDepth {
129      get { return MinDepthParameter.Value.Value; }
130      set { MinDepthParameter.Value.Value = value; }
131    }
132
133    private IFixedValueParameter<IntValue> PgeRptEpochParameter {
134      get { return (IFixedValueParameter<IntValue>)Parameters[PgeRptEpochParameterName]; }
135    }
136    public int PgeRptEpoch {
137      get { return PgeRptEpochParameter.Value.Value; }
138      set { PgeRptEpochParameter.Value.Value = value; }
139    }
140
141    private IFixedValueParameter<IntValue> PgeRptCountParameter {
142      get { return (IFixedValueParameter<IntValue>)Parameters[PgeRptCountParameterName]; }
143    }
144    public int PgeRptCount {
145      get { return PgeRptCountParameter.Value.Value; }
146      set { PgeRptCountParameter.Value.Value = value; }
147    }
148
149    private IFixedValueParameter<IntValue> PgeArchiveCapParameter {
150      get { return (IFixedValueParameter<IntValue>)Parameters[PgeArchiveCapParameterName]; }
151    }
152    public int PgeArchiveCap {
153      get { return PgeArchiveCapParameter.Value.Value; }
154      set { PgeArchiveCapParameter.Value.Value = value; }
155    }
156
157    private IFixedValueParameter<IntValue> PeelCntParameter {
158      get { return (IFixedValueParameter<IntValue>)Parameters[PeelCntParameterName]; }
159    }
160    public int PeelCnt {
161      get { return PeelCntParameter.Value.Value; }
162      set { PeelCntParameter.Value.Value = value; }
163    }
164
165    private IFixedValueParameter<DoubleValue> ZeroEpsilonParameter {
166      get { return (IFixedValueParameter<DoubleValue>)Parameters[ZeroEpsilonParameterName]; }
167    }
168    public double ZeroEpsilon {
169      get { return ZeroEpsilonParameter.Value.Value; }
170      set { ZeroEpsilonParameter.Value.Value = value; }
171    }
172
173    private IFixedValueParameter<DoubleValue> HitRatioParameter {
174      get { return (IFixedValueParameter<DoubleValue>)Parameters[HitRatioParameterName]; }
175    }
176    public double HitRatio {
177      get { return HitRatioParameter.Value.Value; }
178      set { HitRatioParameter.Value.Value = value; }
179    }
180
181    private IFixedValueParameter<StringValue> InitMethodParameter {
182      get { return (IFixedValueParameter<StringValue>)Parameters[InitMethodParameterName]; }
183    }
184    public string InitMethod {
185      get { return InitMethodParameter.Value.Value; }
186      set { InitMethodParameter.Value.Value = value; }
187    }
188
189    private IFixedValueParameter<StringValue> GrowMethodParameter {
190      get { return (IFixedValueParameter<StringValue>)Parameters[GrowMethodParameterName]; }
191    }
192    public string GrowMethod {
193      get { return GrowMethodParameter.Value.Value; }
194      set { GrowMethodParameter.Value.Value = value; }
195    }
196
197    private IFixedValueParameter<StringValue> RootsParameter {
198      get { return (IFixedValueParameter<StringValue>)Parameters[RootsParameterName]; }
199    }
200    public string Roots {
201      get { return RootsParameter.Value.Value; }
202      set { RootsParameter.Value.Value = value; }
203    }
204
205    private IFixedValueParameter<StringValue> NodesParameter {
206      get { return (IFixedValueParameter<StringValue>)Parameters[NodesParameterName]; }
207    }
208    public string Nodes {
209      get { return NodesParameter.Value.Value; }
210      set { NodesParameter.Value.Value = value; }
211    }
212
213    private IFixedValueParameter<StringValue> NonTrigParameter {
214      get { return (IFixedValueParameter<StringValue>)Parameters[NonTrigParameterName]; }
215    }
216    public string NonTrig {
217      get { return NonTrigParameter.Value.Value; }
218      set { NonTrigParameter.Value.Value = value; }
219    }
220
221    private IFixedValueParameter<StringValue> LeafsParameter {
222      get { return (IFixedValueParameter<StringValue>)Parameters[LeafsParameterName]; }
223    }
224    public string Leafs {
225      get { return LeafsParameter.Value.Value; }
226      set { LeafsParameter.Value.Value = value; }
227    }
228    #endregion
229
230    public PGE() {
231
232      base.Problem = new RegressionProblem();
233
234      // algorithm parameters are shown in the GUI
235      Parameters.Add(new FixedValueParameter<IntValue>(MaxIterationsParameterName, new IntValue(50)));
236      Parameters.Add(new FixedValueParameter<IntValue>(MinDepthParameterName, new IntValue(1)));
237      Parameters.Add(new FixedValueParameter<IntValue>(MaxDepthParameterName, new IntValue(6)));
238      Parameters.Add(new FixedValueParameter<IntValue>(MinSizeParameterName, new IntValue(4)));
239      Parameters.Add(new FixedValueParameter<IntValue>(MaxSizeParameterName, new IntValue(50)));
240      Parameters.Add(new FixedValueParameter<IntValue>(EvalrCountParameterName, new IntValue(2)));
241      Parameters.Add(new FixedValueParameter<IntValue>(PeelCntParameterName, new IntValue(3)));
242      Parameters.Add(new FixedValueParameter<IntValue>(PgeArchiveCapParameterName, new IntValue(256)));
243      Parameters.Add(new FixedValueParameter<IntValue>(PgeRptCountParameterName, new IntValue(20)));
244      Parameters.Add(new FixedValueParameter<IntValue>(PgeRptEpochParameterName, new IntValue(1)));
245      Parameters.Add(new FixedValueParameter<IntValue>(MaxGenParameterName, new IntValue(200)));
246
247      Parameters.Add(new FixedValueParameter<StringValue>(InitMethodParameterName, new StringValue("method1")));  // TODO Dropdown
248      Parameters.Add(new FixedValueParameter<StringValue>(GrowMethodParameterName, new StringValue("method1")));
249
250      Parameters.Add(new FixedValueParameter<StringValue>(RootsParameterName, new StringValue("Add")));    // TODO: checkeditemlist
251      Parameters.Add(new FixedValueParameter<StringValue>(NodesParameterName, new StringValue("Add Mul")));  // TODO: checkeditemlist
252      Parameters.Add(new FixedValueParameter<StringValue>(NonTrigParameterName, new StringValue("Add Mul"))); // TODO: checkeditemlist
253      Parameters.Add(new FixedValueParameter<StringValue>(LeafsParameterName, new StringValue("Var ConstantF")));
254
255      Parameters.Add(new FixedValueParameter<DoubleValue>(ZeroEpsilonParameterName, new DoubleValue(0.00001)));
256      Parameters.Add(new FixedValueParameter<DoubleValue>(HitRatioParameterName, new DoubleValue(0.01)));
257    }
258
259
260    [StorableConstructor]
261    public PGE(bool deserializing) : base(deserializing) { }
262
263
264    public PGE(PGE original, Cloner cloner) : base(original, cloner) {
265      // nothing to clone
266    }
267
268    public override IDeepCloneable Clone(Cloner cloner) {
269      return new PGE(this, cloner);
270    }
271
272    protected override void Run(CancellationToken cancellationToken) {
273      Log log = new Log();
274      Results.Add(new Result("Log", log));
275      var iterationsResult = new IntValue(0);
276      Results.Add(new Result("Iteration", iterationsResult));
277      var bestTestScoreResult = new IntValue(0); // TODO: why is test score an int?
278      Results.Add(new Result("Best test score", bestTestScoreResult));
279      var testScoresTable = new DataTable("Test scores");
280      var bestTestScoreRow = new DataRow("Best test score");
281      var curTestScoreRow = new DataRow("Current test score");
282      testScoresTable.Rows.Add(bestTestScoreRow);
283      testScoresTable.Rows.Add(curTestScoreRow);
284      Results.Add(new Result("Test scores", testScoresTable));
285      var lengthsTable = new DataTable("Lengths");
286      var len1Row = new DataRow("Length 1");
287      var len2Row = new DataRow("Length 2");
288      lengthsTable.Rows.Add(len1Row);
289      lengthsTable.Rows.Add(len2Row);
290      Results.Add(new Result("Lengths", lengthsTable));
291
292      var bestSolutionResult = new Result("Best solution", typeof(IRegressionSolution));
293      Results.Add(bestSolutionResult);
294
295      // TODO: the following is potentially problematic for other go processes run on the same machine at the same time
296      // shouldn't be problematic bc is inherited only, normally only child processes are affected
297      Environment.SetEnvironmentVariable("GOGC", "off");
298      Environment.SetEnvironmentVariable("GODEBUG", "cgocheck=0");
299      Environment.SetEnvironmentVariable("CGO_ENABLED", "1");
300      Environment.SetEnvironmentVariable("PGEDEBUG", "0");
301
302
303      //Constants
304      int sortType = 0; // TODO what's sort type?
305      string problemTypeString = "benchmark";
306      int numProc = 12;
307      string problemName = Problem.ProblemData.Name;
308
309
310      var problemData = Problem.ProblemData;
311      var variables = problemData.AllowedInputVariables.Concat(new string[] { problemData.TargetVariable });
312      // no idea why the following are IntPtr, this should not be necessary for marshalling, it should be ok to just send the double[,]
313      int nTrainData;
314      int nTestData;
315      IntPtr trainData = GetData(problemData.Dataset, variables, problemData.TrainingIndices, out nTrainData);
316      IntPtr testData = GetData(problemData.Dataset, variables, problemData.TestIndices, out nTestData);
317
318      nTrainData = Problem.ProblemData.TrainingPartition.Size;
319      nTestData = Problem.ProblemData.TestPartition.Size;
320
321      if (problemData.AllowedInputVariables.Any(iv => iv.Contains(" ")))
322        throw new NotSupportedException("PGE does not support variable names which contain spaces");
323
324      var inputVariableNames = string.Join(" ", problemData.AllowedInputVariables);
325
326      IntPtr cIndepNames = Marshal.StringToHGlobalAnsi(inputVariableNames);
327      IntPtr cDependentNames = Marshal.StringToHGlobalAnsi(problemData.TargetVariable);
328      // Dependent- and Independentnames are the variables from the test/train data, e.g. from "Korns_02.trn" indep: x y z v w  dep: f(xs)
329
330      IntPtr cInitMethod = Marshal.StringToHGlobalAnsi(InitMethod);
331      IntPtr cGrowMethod = Marshal.StringToHGlobalAnsi(GrowMethod);
332
333      IntPtr cRoots = Marshal.StringToHGlobalAnsi(Roots);
334      IntPtr cNodes = Marshal.StringToHGlobalAnsi(Nodes);
335      IntPtr cNonTrig = Marshal.StringToHGlobalAnsi(NonTrig);
336      IntPtr cLeafs = Marshal.StringToHGlobalAnsi(Leafs);
337
338      IntPtr cName = Marshal.StringToHGlobalAnsi(problemName);
339      IntPtr cProblemTypeString = Marshal.StringToHGlobalAnsi(problemTypeString);
340
341
342      AddTestData(cIndepNames, cDependentNames, testData, nTestData);
343      AddTrainData(cIndepNames, cDependentNames, trainData, nTrainData);
344
345      int numberOfUseableVariables = problemData.AllowedInputVariables.Count();
346      IntPtr cUseableVars = GetUsableVars(numberOfUseableVariables);
347
348      InitSearch(MaxGen, PgeRptEpoch, PgeRptCount, PgeArchiveCap, PeelCnt, EvalrCount, ZeroEpsilon, cInitMethod, cGrowMethod, sortType);
349
350      // cUsableVars: list of indices into independent variables
351      InitTreeParams(cRoots, cNodes, cNonTrig, cLeafs, cUseableVars, numberOfUseableVariables, MaxSize, MinSize, MaxDepth, MinDepth);
352
353     
354      InitProblem(cName, MaxIterations, HitRatio,
355        searchVar: 0,  // SearchVar: index of dependent variables (this is always zero because we only have one target variable)
356        ProblemTypeString: cProblemTypeString, numProcs: numProc);
357
358      for (int iter = 1; iter <= MaxIterations; iter++) {
359        iterationsResult.Value = iter;
360
361        int nResults = StepW();
362
363        for (int iResult = 0; iResult < nResults; iResult++) {
364          int nobestpush = 0;       //bool
365          int bestNewMinError = 0;  //bool
366          int bestlen1 = 0;
367          int bestlen2 = 0;
368          int nCoeff = 0;
369          int testScore = 0;
370
371          IntPtr eqn = GetStepResult(out nobestpush, out bestNewMinError, out bestlen1, out bestlen2, out testScore, out nCoeff);
372          string eqnStr = Marshal.PtrToStringAnsi(eqn);
373
374          double[] coeff = new double[nCoeff];
375          for (int iCoeff = 0; iCoeff < nCoeff; iCoeff++) {
376            coeff[iCoeff] = GetCoeffResult();
377          }
378          log.LogMessage("Push/Pop (" + iResult + ", " + bestlen1 + ", " + bestlen2 + ", " + testScore + ", noBestPush: " + (nobestpush > 0) + ", bestNewMin: " + (bestNewMinError > 0) + ") " + eqnStr + " coeff: " + string.Join(" ", coeff));
379
380          if (bestNewMinError > 0) {
381            // update best quality
382            bestTestScoreResult.Value = testScore;
383            var sol = CreateSolution(problemData, eqnStr, coeff, problemData.AllowedInputVariables.ToArray());
384            bestSolutionResult.Value = sol;
385          }
386          bestTestScoreRow.Values.Add(bestTestScoreResult.Value); // always add the current best test score to data row
387          curTestScoreRow.Values.Add(testScore);
388          len1Row.Values.Add(bestlen1);
389          len2Row.Values.Add(bestlen2);
390        }
391
392        if (cancellationToken.IsCancellationRequested) break;
393      }
394
395      Marshal.FreeHGlobal(cUseableVars);
396      Marshal.FreeHGlobal(trainData);
397      Marshal.FreeHGlobal(testData);
398
399      Marshal.FreeHGlobal(cIndepNames);
400      Marshal.FreeHGlobal(cDependentNames);
401
402      Marshal.FreeHGlobal(cInitMethod);
403      Marshal.FreeHGlobal(cGrowMethod);
404
405      Marshal.FreeHGlobal(cRoots);
406      Marshal.FreeHGlobal(cNodes);
407      Marshal.FreeHGlobal(cNonTrig);
408      Marshal.FreeHGlobal(cLeafs);
409
410      Marshal.FreeHGlobal(cName);
411      Marshal.FreeHGlobal(cProblemTypeString);
412
413      // Results.Add(new Result("Execution time", new TimeSpanValue(this.ExecutionTime)));
414    }
415
416    private static readonly Regex varRegex = new Regex(@"X_(\d)+");
417    private static readonly Regex coeffRegex = new Regex(@"C_(\d)+");
418
419    private IRegressionSolution CreateSolution(IRegressionProblemData problemData, string eqnStr, double[] coeff, string[] usableVariables) {
420      // coefficients are named e.g. "C_0" in the PGE expressions
421      // -> replace all patterns "C_\d" by the corresponding coefficients
422      var match = coeffRegex.Match(eqnStr);
423      while (match.Success) {
424        var coeffIdx = int.Parse(match.Groups[1].ToString());
425        eqnStr = eqnStr.Substring(0, match.Index) +
426          "(" + coeff[coeffIdx].ToString(System.Globalization.CultureInfo.InvariantCulture) + ")" +
427          eqnStr.Substring(match.Index + match.Length);
428        match = coeffRegex.Match(eqnStr);
429      }
430
431      // variables are named e.g. "X_0" in the PGE expressions
432      // -> replace all patterns "X_\d" by the corresponding variable name
433      match = varRegex.Match(eqnStr);
434      while (match.Success) {
435        var varIdx = int.Parse(match.Groups[1].ToString());
436        eqnStr = eqnStr.Substring(0, match.Index) +
437          "'" + usableVariables[varIdx] + "'" +
438          eqnStr.Substring(match.Index + match.Length);
439        match = varRegex.Match(eqnStr);
440      }
441
442      var parser = new InfixExpressionParser();
443      var tree = parser.Parse(eqnStr);
444      var model = new SymbolicRegressionModel(problemData.TargetVariable, tree, new SymbolicDataAnalysisExpressionTreeLinearInterpreter());
445      return model.CreateRegressionSolution((IRegressionProblemData)problemData.Clone());
446    }
447
448    public override bool SupportsPause {
449      get { return false; }
450    }
451
452    private static IntPtr GetUsableVars(int n) {
453      long[] vars = new long[n];
454
455      for (int i = 0; i < n; i++) {
456        vars[i] = i;
457      }
458
459      IntPtr usableVars = Marshal.AllocHGlobal(sizeof(long) * n);
460      Marshal.Copy(vars, 0, usableVars, n);
461
462      return usableVars;
463    }
464
465    private static IntPtr GetData(IDataset ds, IEnumerable<string> variableNames, IEnumerable<int> rows, out int n) {
466
467      var dim = variableNames.Count();
468      double[] val = new double[rows.Count() * dim];
469      int r = 0;
470      foreach (var row in rows) {
471        int c = 0;
472        foreach (var var in variableNames) {
473          val[r * dim + c] = ds.GetDoubleValue(var, r);
474          c++;
475        }
476        r++;
477      }
478
479      n = val.Length;
480
481      // TODO: seems strange to marshal this explicitly, we can just send the data over to PGE
482      IntPtr data = Marshal.AllocHGlobal(sizeof(double) * val.Length);
483      Marshal.Copy(val, 0, data, val.Length);
484
485      return data;
486    }
487  }
488}
Note: See TracBrowser for help on using the repository browser.