Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
10/03/19 12:30:19 (5 years ago)
Author:
gkronber
Message:

#2994 continued refactoring and extended unit tests. Interval calculation still fails for some edge cases (mainly for undefined behaviour). VectorEvaluator and VectorAutoDiffEvaluator produce the same results as the LinearInterpreter. TODO: check gradient calculation

Location:
branches/2994-AutoDiffForIntervals/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4
Files:
1 added
10 edited

Legend:

Unmodified
Added
Removed
  • branches/2994-AutoDiffForIntervals/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj

    r17297 r17303  
    182182    <Compile Include="Interpreter\VectorAutoDiffEvaluator.cs" />
    183183    <Compile Include="Interpreter\VectorEvaluator.cs" />
     184    <Compile Include="Interpreter\VectorOfAlgebraic.cs" />
    184185    <Compile Include="Selectors\DiversitySelector.cs" />
    185186    <Compile Include="SymbolicDataAnalysisExpressionTreeAverageSimilarityCalculator.cs" />
  • branches/2994-AutoDiffForIntervals/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/Algebraic.cs

    r17295 r17303  
    1212    public static T IntRoot<T>(this T a, int r) where T : IAlgebraicType<T> { a.AssignIntRoot(a.Clone(), r); return a; }
    1313
    14     internal static T Min<T>(T a, T b) where T : IAlgebraicType<T> { return a.Clone().AssignMin(b); }
    15     internal static T Max<T>(T a, T b) where T : IAlgebraicType<T> { return a.Clone().AssignMax(b); }
    16 
    1714    // public static T Max<T>(T a, T b) where T : IAlgebraicType<T> {
    1815    //   // ((a + b) + abs(b - a)) / 2
  • branches/2994-AutoDiffForIntervals/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/AlgebraicDouble.cs

    r17298 r17303  
    55  // algebraic type wrapper for a double value
    66  [DebuggerDisplay("{Value}")]
    7   public sealed class AlgebraicDouble : IAlgebraicType<AlgebraicDouble> {
     7  public sealed class AlgebraicDouble : IComparableAlgebraicType<AlgebraicDouble> {
    88    public static implicit operator AlgebraicDouble(double value) { return new AlgebraicDouble(value); }
    99    public static implicit operator double(AlgebraicDouble value) { return value.Value; }
     
    3535    public AlgebraicDouble AssignIntPower(AlgebraicDouble a, int p) { Value = Math.Pow(a.Value, p); return this; }
    3636    public AlgebraicDouble AssignIntRoot(AlgebraicDouble a, int r) { Value = IntRoot(a.Value, r); return this; }
    37     public AlgebraicDouble AssignMin(AlgebraicDouble other) { Value = Math.Min(Value, other.Value); return this; }
    38     public AlgebraicDouble AssignMax(AlgebraicDouble other) { Value = Math.Max(Value, other.Value); return this; }
     37    //public AlgebraicDouble AssignMin(AlgebraicDouble other) { Value = Math.Min(Value, other.Value); return this; }
     38    //public AlgebraicDouble AssignMax(AlgebraicDouble other) { Value = Math.Max(Value, other.Value); return this; }
    3939    public AlgebraicDouble AssignAbs(AlgebraicDouble a) { Value = Math.Abs(a.Value); return this; }
    4040    public AlgebraicDouble AssignSgn(AlgebraicDouble a) { Value = double.IsNaN(a.Value) ? double.NaN : Math.Sign(a.Value); return this; }
     
    5151      return Value.ToString();
    5252    }
     53
     54    public int CompareTo(AlgebraicDouble other) {
     55      return Value.CompareTo(other.Value);
     56    }
    5357  }
    5458}
  • branches/2994-AutoDiffForIntervals/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/AlgebraicDoubleVector.cs

    r17298 r17303  
    66  // a simple vector as an algebraic type
    77  [DebuggerDisplay("DoubleVector(len={Length}): {string.}")]
     8  [Obsolete("Use VectorOfAlgebraic instead")]
    89  public class AlgebraicDoubleVector : IAlgebraicType<AlgebraicDoubleVector> {
    910    private double[] arr;
     
    4041    public AlgebraicDoubleVector AssignIntPower(AlgebraicDoubleVector a, int p) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Pow(a.arr[i], p); } return this; }
    4142    public AlgebraicDoubleVector AssignIntRoot(AlgebraicDoubleVector a, int r) { for (int i = 0; i < arr.Length; ++i) { arr[i] = IntRoot(a.arr[i], r); } return this; }
    42     public AlgebraicDoubleVector AssignMin(AlgebraicDoubleVector other) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Min(arr[i], other.arr[i]); } return this; }
    43     public AlgebraicDoubleVector AssignMax(AlgebraicDoubleVector other) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Max(arr[i], other.arr[i]); } return this; }
     43    //public AlgebraicDoubleVector AssignMin(AlgebraicDoubleVector other) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Min(arr[i], other.arr[i]); } return this; }
     44    //public AlgebraicDoubleVector AssignMax(AlgebraicDoubleVector other) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Max(arr[i], other.arr[i]); } return this; }
    4445    public AlgebraicDoubleVector AssignAbs(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Abs(a.arr[i]); } return this; }
    4546    public AlgebraicDoubleVector AssignSgn(AlgebraicDoubleVector a) { for (int i = 0; i < arr.Length; ++i) { arr[i] = Math.Sign(a.arr[i]); } return this; }
  • branches/2994-AutoDiffForIntervals/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/AlgebraicInterval.cs

    r17295 r17303  
    4747      var v4 = high.Clone().Mul(a.high);
    4848
    49       low = Min(Min(v1, v2), Min(v3, v4));
    50       high = Max(Max(v1, v2), Max(v3, v4));
    51 
    52       return this;
    53     }
    54 
    55 
    56     private static MultivariateDual<AlgebraicDouble> Min(MultivariateDual<AlgebraicDouble> a, MultivariateDual<AlgebraicDouble> b) {
    57       return a.Value < b.Value ? a : b;
    58     }
    59     private static MultivariateDual<AlgebraicDouble> Max(MultivariateDual<AlgebraicDouble> a, MultivariateDual<AlgebraicDouble> b) {
    60       return a.Value > b.Value ? a : b;
     49      AssignLowAndHigh(v1, v2, v3, v4);
     50
     51      return this;
    6152    }
    6253
     
    133124        if (a.Contains(0.0)) {
    134125          low = new MultivariateDual<AlgebraicDouble>(0.0);
    135           high = a.low.IntPower(p).AssignMax(a.high.IntPower(p));
    136         } else {
    137           var lowPower = a.low.IntPower(p);
    138           var highPower = a.high.IntPower(p);
    139           low = lowPower.AssignMin(highPower);
    140           high = lowPower.AssignMax(highPower);
     126          AssignMax(high, a.low.IntPower(p), a.high.IntPower(p));
     127        } else {
     128          AssignLowAndHigh(a.low.IntPower(p), a.high.IntPower(p));
    141129        }
    142130      } else {
     
    148136          var lowPower = a.low.IntPower(p);
    149137          var highPower = a.high.IntPower(p);
    150           low = lowPower.AssignMin(highPower);
    151           high = lowPower.AssignMax(highPower);
     138          AssignMin(low, lowPower, highPower);
     139          AssignMax(high, lowPower, highPower);
    152140        }
    153141      }
     
    217205
    218206      // assume low and high are in the same quadrant
    219       low = Algebraic.Min(a.LowerBound.Clone().Sin(), a.UpperBound.Clone().Sin());
    220       high = Algebraic.Max(a.LowerBound.Clone().Sin(), a.UpperBound.Clone().Sin());
     207      AssignLowAndHigh(a.LowerBound.Clone().Sin(), a.UpperBound.Clone().Sin()); // AssignLowAndHigh determines the lower and higher value
    221208
    222209      // override min and max if necessary
     
    260247    public AlgebraicInterval AssignAbs(AlgebraicInterval a) {
    261248      if (a.Contains(0.0)) {
     249        low.Assign(new MultivariateDual<AlgebraicDouble>(0.0)); // lost gradient for lower bound
     250        AssignMax(high, a.low.Clone().Abs(), a.high.Clone().Abs());
     251      } else {
    262252        var abslow = a.low.Clone().Abs();
    263253        var abshigh = a.high.Clone().Abs();
    264         a.high.Assign(Algebraic.Max(abslow, abshigh));
    265         a.low.Assign(new MultivariateDual<AlgebraicDouble>(0.0)); // lost gradient for lower bound
    266       } else {
    267         var abslow = a.low.Clone().Abs();
    268         var abshigh = a.high.Clone().Abs();
    269         a.low.Assign(Algebraic.Min(abslow, abshigh));
    270         a.high.Assign(Algebraic.Max(abslow, abshigh));
     254        AssignLowAndHigh(abslow, abshigh);
    271255      }
    272256      return this;
     
    279263    }
    280264
    281     public AlgebraicInterval AssignMin(AlgebraicInterval other) {
    282       low.AssignMin(other.low);
    283       high.AssignMin(other.high);
    284       return this;
    285     }
    286 
    287     public AlgebraicInterval AssignMax(AlgebraicInterval other) {
    288       low.AssignMax(other.low);
    289       high.AssignMax(other.high);
    290       return this;
    291     }
     265
     266    #region helper
     267    private void AssignMin(MultivariateDual<AlgebraicDouble> dest, MultivariateDual<AlgebraicDouble> a, MultivariateDual<AlgebraicDouble> b) {
     268      if (a.Value.CompareTo(b.Value) <= 0) {
     269        dest.Assign(a);
     270      } else {
     271        dest.Assign(b);
     272      }
     273    }
     274    private void AssignMax(MultivariateDual<AlgebraicDouble> dest, MultivariateDual<AlgebraicDouble> a, MultivariateDual<AlgebraicDouble> b) {
     275      if (a.Value.CompareTo(b.Value) <= 0) {
     276        dest.Assign(b);
     277      } else {
     278        dest.Assign(a);
     279      }
     280    }
     281
     282    // determines the smaller and larger value and sets low and high accordingly
     283    private void AssignLowAndHigh(MultivariateDual<AlgebraicDouble> a, MultivariateDual<AlgebraicDouble> b) {
     284      // we must make sure that low and high are different objects when a == b
     285      if (a.Value.CompareTo(b.Value) == 0) {
     286        low.Assign(a);
     287        high.Assign(b);
     288      } else {
     289        AssignMin(low, a, b);
     290        AssignMax(high, a, b);
     291      }
     292    }
     293    private void AssignLowAndHigh(params MultivariateDual<AlgebraicDouble>[] xs) {
     294      if (xs.Length <= 2) throw new ArgumentException("need at least 3 arguments");
     295      AssignLowAndHigh(xs[0], xs[1]);
     296      for(int i=2;i<xs.Length;i++) {
     297        // we must make sure that low and high are different objects when a == b
     298        if (low.Value.CompareTo(xs[i].Value) > 0) low.Assign(xs[i]);
     299        if (high.Value.CompareTo(xs[i].Value) < 0) high.Assign(xs[i]);
     300      }
     301    }
     302    #endregion
     303
    292304  }
    293305}
  • branches/2994-AutoDiffForIntervals/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/AlgebraicSparseVector.cs

    r17298 r17303  
    109109    }
    110110
    111     public AlgebraicSparseVector<K, T> AssignMin(AlgebraicSparseVector<K, T> other) {
    112       // assumes that keys without a matching key in other are zero and vice versa
    113       foreach (var kvp in elems) if (!other.elems.ContainsKey(kvp.Key)) kvp.Value.AssignMin(kvp.Value.Zero); // min(v, 0)
    114       foreach (var kvp in other.elems) {
    115         if (elems.TryGetValue(kvp.Key, out T value))
    116           value.AssignMin(kvp.Value);
    117         else
    118           elems.Add(kvp.Key, kvp.Value.Zero.AssignMin(kvp.Value));
    119       }
    120       return this;
    121     }
    122 
    123     public AlgebraicSparseVector<K, T> AssignMax(AlgebraicSparseVector<K, T> other) {
    124       // assumes that keys without a matching key in other are zero and vice versa
    125       foreach (var kvp in elems) if (!other.elems.ContainsKey(kvp.Key)) kvp.Value.AssignMax(kvp.Value.Zero); // max(v, 0)
    126       foreach (var kvp in other.elems) {
    127         if (elems.TryGetValue(kvp.Key, out T value))
    128           value.AssignMax(kvp.Value);
    129         else
    130           elems.Add(kvp.Key, kvp.Value.Zero.AssignMax(kvp.Value));
    131       }
    132       return this;
    133     }
    134 
    135 
    136111    public AlgebraicSparseVector<K, T> Clone() {
    137112      return new AlgebraicSparseVector<K, T>(this);
  • branches/2994-AutoDiffForIntervals/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/Dual.cs

    r17295 r17303  
    44namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
    55  public class Dual<V> : IAlgebraicType<Dual<V>>
    6     where V : IAlgebraicType<V> {
     6    where V : IComparableAlgebraicType<V> {
    77    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    88    private V v;
     
    5454
    5555    public Dual<V> AssignMin(Dual<V> other) {
    56       throw new NotImplementedException();
     56      if(v.CompareTo(other.v) > 0) {
     57        v.Assign(other.v);
     58        dv.Assign(other.dv);
     59      }
     60      return this;
    5761    }
    5862
    5963    public Dual<V> AssignMax(Dual<V> other) {
    60       throw new NotImplementedException();
     64      if (v.CompareTo(other.v) <= 0) {
     65        v.Assign(other.v);
     66        dv.Assign(other.dv);
     67      }
     68      return this;
    6169    }
    6270  }
  • branches/2994-AutoDiffForIntervals/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/IAlgebraicType.cs

    r17295 r17303  
    1 namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
     1using System;
     2
     3namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
    24  public interface IAlgebraicType<T> {
    35    T Zero { get; } // Zero and One must create new objects
     
    2123    T AssignIntRoot(T a, int r);
    2224    T AssignSgn(T a); // set this to sign(a)
    23     T AssignMin(T other); // set this min(this, other)
    24     T AssignMax(T other); // set this max(this, other)
     25    //T AssignMin(T other); // set this min(this, other)
     26    //T AssignMax(T other); // set this max(this, other)
    2527    T Clone();
    2628  }
     29
     30  public interface IComparableAlgebraicType<T> : IAlgebraicType<T>, IComparable<T> {
     31
     32  }
    2733}
  • branches/2994-AutoDiffForIntervals/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/MultivariateDual.cs

    r17297 r17303  
    1717    public AlgebraicSparseVector<object, V> Gradient => dv; // <key,value> partial derivative identified via the key
    1818
     19
    1920    private MultivariateDual(MultivariateDual<V> orig) { this.v = orig.v.Clone(); this.dv = orig.dv.Clone(); }
     21
     22    /// <summary>
     23    /// Constructor which sets value and derivative to zero
     24    /// </summary>
     25    public MultivariateDual() {
     26      v = new V(); // assumed to be zero
     27      dv = new AlgebraicSparseVector<object, V>();
     28    }
    2029
    2130    /// <summary>
     
    8392    public MultivariateDual<V> AssignAbs(MultivariateDual<V> a) { v.AssignAbs(a.v); dv.Assign(a.dv).Scale(a.v.Clone().Sgn()); return this; }      // abs(f(x))' = f(x)*f'(x) / |f(x)|  doesn't work for intervals     
    8493    public MultivariateDual<V> AssignSgn(MultivariateDual<V> a) { v.AssignSgn(a.v); dv = a.dv.Zero; return this; } // sign(f(x))' = 0;     
    85 
    86     public MultivariateDual<V> AssignMin(MultivariateDual<V> other) {
    87       throw new NotImplementedException();
    88     }
    89 
    90     public MultivariateDual<V> AssignMax(MultivariateDual<V> other) {
    91       throw new NotImplementedException();
    92     }
    9394  }
    9495}
  • branches/2994-AutoDiffForIntervals/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/VectorAutoDiffEvaluator.cs

    r17296 r17303  
    55
    66namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
    7   public sealed class VectorAutoDiffEvaluator : InterpreterBase<MultivariateDual<AlgebraicDoubleVector>> {
     7  public sealed class VectorAutoDiffEvaluator : InterpreterBase<VectorOfAlgebraic<MultivariateDual<AlgebraicDouble>>> {
    88    private const int BATCHSIZE = 128;
    99    [ThreadStatic]
     
    5757      for (rowIndex = 0; rowIndex < roundedTotal; rowIndex += BATCHSIZE) {
    5858        Evaluate(code);
    59         code[0].value.Value.CopyTo(fi, rowIndex, BATCHSIZE);
    6059
    61         // TRANSPOSE into JAC
    62         var g = code[0].value.Gradient;
    63         for (int j = 0; j < nParams; ++j) {
    64           if (g.Elements.TryGetValue(j, out AlgebraicDoubleVector v)) {
    65             v.CopyColumnTo(jac, j, rowIndex, BATCHSIZE);
    66           } else {
    67             for (int r = 0; r < BATCHSIZE; r++) jac[rowIndex + r, j] = 0.0;
     60        // code[0].value.Value.CopyTo(fi, rowIndex, BATCHSIZE);
     61        var v = code[0].value;
     62        for (int k = 0; k < BATCHSIZE; k++) {
     63          fi[rowIndex + k] = v[k].Value.Value;
     64
     65          // copy gradient to Jacobian
     66          var g = v[k].Gradient;
     67          for (int j = 0; j < nParams; ++j) {
     68            if (g.Elements.TryGetValue(j, out AlgebraicDouble gj)) {
     69              jac[rowIndex + k, j] = gj.Value;
     70            } else {
     71              jac[rowIndex + k, j] = 0.0;
     72            }
    6873          }
    6974        }
     
    7277      if (remainingRows > 0) {
    7378        Evaluate(code);
    74         code[0].value.Value.CopyTo(fi, roundedTotal, remainingRows);
     79        // code[0].value.Value.CopyTo(fi, roundedTotal, remainingRows);
     80        var v = code[0].value;
     81        for (int k = 0; k < remainingRows; k++) {
     82          fi[roundedTotal + k] = v[k].Value.Value;
    7583
    76         var g = code[0].value.Gradient;
    77         for (int j = 0; j < nParams; ++j)
    78           if (g.Elements.TryGetValue(j, out AlgebraicDoubleVector v)) {
    79             v.CopyColumnTo(jac, j, roundedTotal, remainingRows);
    80           } else {
    81             for (int r = 0; r < remainingRows; r++) jac[roundedTotal + r, j] = 0.0;
     84          var g = v[k].Gradient;
     85          for (int j = 0; j < nParams; ++j) {
     86            if (g.Elements.TryGetValue(j, out AlgebraicDouble gj)) {
     87              jac[roundedTotal + k, j] = gj.Value;
     88            } else {
     89              jac[roundedTotal + k, j] = 0.0;
     90            }
    8291          }
     92        }
    8393      }
    8494    }
    8595
    8696    protected override void InitializeInternalInstruction(ref Instruction instruction, ISymbolicExpressionTreeNode node) {
    87       var zero = new AlgebraicDoubleVector(BATCHSIZE);
    88       instruction.value = new MultivariateDual<AlgebraicDoubleVector>(zero);
     97      instruction.value = new VectorOfAlgebraic<MultivariateDual<AlgebraicDouble>>(BATCHSIZE).Zero; // XXX zero needed?
    8998    }
    9099
    91100    protected override void InitializeTerminalInstruction(ref Instruction instruction, ConstantTreeNode constant) {
    92       var g_arr = new double[BATCHSIZE];
    93101      if (node2paramIdx.TryGetValue(constant, out var paramIdx)) {
    94         for (int i = 0; i < BATCHSIZE; i++) g_arr[i] = 1.0;
    95         var g = new AlgebraicDoubleVector(g_arr);
    96         instruction.value = new MultivariateDual<AlgebraicDoubleVector>(new AlgebraicDoubleVector(BATCHSIZE), paramIdx, g); // only a single column for the gradient
     102        instruction.value = new VectorOfAlgebraic<MultivariateDual<AlgebraicDouble>>(BATCHSIZE);
     103        for (int k = 0; k < BATCHSIZE; k++) {
     104          instruction.value[k] = new MultivariateDual<AlgebraicDouble>(constant.Value, paramIdx, 1.0); // gradient is 1.0 for all elements
     105        }
    97106      } else {
    98         instruction.value = new MultivariateDual<AlgebraicDoubleVector>(new AlgebraicDoubleVector(BATCHSIZE));
     107        instruction.value = new VectorOfAlgebraic<MultivariateDual<AlgebraicDouble>>(BATCHSIZE);
     108        for (int k = 0; k < BATCHSIZE; k++) {
     109          instruction.value[k] = new MultivariateDual<AlgebraicDouble>(constant.Value); // zero gradient
     110        }
    99111      }
    100112
    101       instruction.dblVal = constant.Value;
    102       instruction.value.Value.AssignConstant(instruction.dblVal);
     113      instruction.dblVal = constant.Value; // also store the parameter value in the instruction (not absolutely necessary, will not be used)
    103114    }
    104115
     
    115126      if (node2paramIdx.ContainsKey(variable)) {
    116127        paramIdx = node2paramIdx[variable];
    117         var f = new AlgebraicDoubleVector(BATCHSIZE);
    118         var g = new AlgebraicDoubleVector(BATCHSIZE);
    119         instruction.value = new MultivariateDual<AlgebraicDoubleVector>(f, paramIdx, g);
     128        instruction.value = new VectorOfAlgebraic<MultivariateDual<AlgebraicDouble>>(BATCHSIZE);
     129        for(int k=0;k<BATCHSIZE;k++) {
     130          instruction.value[k] = new MultivariateDual<AlgebraicDouble>(0.0, paramIdx, 0.0); // values are set in LoadVariable()
     131        }
    120132      } else {
    121133        var f = new AlgebraicDoubleVector(BATCHSIZE);
    122         instruction.value = new MultivariateDual<AlgebraicDoubleVector>(f);
     134        instruction.value = new VectorOfAlgebraic<MultivariateDual<AlgebraicDouble>>(BATCHSIZE);
     135        for (int k = 0; k < BATCHSIZE; k++) {
     136          instruction.value[k] = new MultivariateDual<AlgebraicDouble>(0.0); // values are set in LoadVariable()
     137        }
    123138      }
    124139
    125       instruction.dblVal = variable.Weight;
     140      instruction.dblVal = variable.Weight; 
    126141      instruction.data = new object[] { data, paramIdx };
    127142    }
     
    131146      var data = (double[])((object[])a.data)[0];
    132147
    133       for (int i = rowIndex; i < rows.Length && i - rowIndex < BATCHSIZE; i++) a.value.Value[i - rowIndex] = data[rows[i]];
    134       a.value.Scale(a.dblVal);
     148      for (int i = rowIndex; i < rows.Length && i - rowIndex < BATCHSIZE; i++) {
     149        a.value[i - rowIndex].Value.Assign(a.dblVal * data[rows[i]]);
     150      }
    135151
    136152      if (paramIdx >= 0) {
    137153        // update gradient with variable values
    138         var g = a.value.Gradient.Elements[paramIdx];
    139154        for (int i = rowIndex; i < rows.Length && i - rowIndex < BATCHSIZE; i++) {
    140           g[i - rowIndex] = data[rows[i]];
     155          a.value[i - rowIndex].Gradient.Elements[paramIdx].Assign(data[rows[i]]);
    141156        }
    142157      }
Note: See TracChangeset for help on using the changeset viewer.