private void CalculateModel(IDataset ds, IEnumerable <int> rows, bool scaleInputs = true)
        {
            this.trainingDataset = (IDataset)ds.Clone();
            this.trainingRows    = rows.ToArray();
            this.inputScaling    = scaleInputs ? new Scaling(ds, allowedInputVariables, rows) : null;

            x = GetData(ds, this.allowedInputVariables, this.trainingRows, this.inputScaling);

            IEnumerable <double> y;

            y = ds.GetDoubleValues(targetVariable, rows);

            int n = x.GetLength(0);

            // calculate cholesky decomposed (lower triangular) covariance matrix
            var cov = covarianceFunction.GetParameterizedCovarianceFunction(covarianceParameter, Enumerable.Range(0, x.GetLength(1)));

            this.l = CalculateL(x, cov, sqrSigmaNoise);

            // calculate mean
            var mean = meanFunction.GetParameterizedMeanFunction(meanParameter, Enumerable.Range(0, x.GetLength(1)));

            double[] m = Enumerable.Range(0, x.GetLength(0))
                         .Select(r => mean.Mean(x, r))
                         .ToArray();

            // calculate sum of diagonal elements for likelihood
            double diagSum = Enumerable.Range(0, n).Select(i => Math.Log(l[i, i])).Sum();

            // solve for alpha
            double[] ym = y.Zip(m, (a, b) => a - b).ToArray();

            int info;

            alglib.densesolverreport denseSolveRep;

            alglib.spdmatrixcholeskysolve(l, n, false, ym, out info, out denseSolveRep, out alpha);
            for (int i = 0; i < alpha.Length; i++)
            {
                alpha[i] = alpha[i] / sqrSigmaNoise;
            }
            negativeLogLikelihood = 0.5 * Util.ScalarProd(ym, alpha) + diagSum + (n / 2.0) * Math.Log(2.0 * Math.PI * sqrSigmaNoise);

            // derivatives
            int nAllowedVariables = x.GetLength(1);

            alglib.matinvreport matInvRep;
            double[,] lCopy = new double[l.GetLength(0), l.GetLength(1)];
            Array.Copy(l, lCopy, lCopy.Length);

            alglib.spdmatrixcholeskyinverse(ref lCopy, n, false, out info, out matInvRep);
            if (info != 1)
            {
                throw new ArgumentException("Can't invert matrix to calculate gradients.");
            }
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j <= i; j++)
                {
                    lCopy[i, j] = lCopy[i, j] / sqrSigmaNoise - alpha[i] * alpha[j];
                }
            }

            double noiseGradient = sqrSigmaNoise * Enumerable.Range(0, n).Select(i => lCopy[i, i]).Sum();

            double[] meanGradients = new double[meanFunction.GetNumberOfParameters(nAllowedVariables)];
            for (int k = 0; k < meanGradients.Length; k++)
            {
                var meanGrad = Enumerable.Range(0, alpha.Length)
                               .Select(r => mean.Gradient(x, r, k));
                meanGradients[k] = -Util.ScalarProd(meanGrad, alpha);
            }

            double[] covGradients = new double[covarianceFunction.GetNumberOfParameters(nAllowedVariables)];
            if (covGradients.Length > 0)
            {
                for (int i = 0; i < n; i++)
                {
                    for (int j = 0; j < i; j++)
                    {
                        var g = cov.CovarianceGradient(x, i, j).ToArray();
                        for (int k = 0; k < covGradients.Length; k++)
                        {
                            covGradients[k] += lCopy[i, j] * g[k];
                        }
                    }

                    var gDiag = cov.CovarianceGradient(x, i, i).ToArray();
                    for (int k = 0; k < covGradients.Length; k++)
                    {
                        // diag
                        covGradients[k] += 0.5 * lCopy[i, i] * gDiag[k];
                    }
                }
            }

            hyperparameterGradients =
                meanGradients
                .Concat(covGradients)
                .Concat(new double[] { noiseGradient }).ToArray();
        }
    private void CalculateModel(IDataset ds, IEnumerable<int> rows, bool scaleInputs = true) {
      this.trainingDataset = (IDataset)ds.Clone();
      this.trainingRows = rows.ToArray();
      this.inputScaling = scaleInputs ? new Scaling(ds, allowedInputVariables, rows) : null;

      x = GetData(ds, this.allowedInputVariables, this.trainingRows, this.inputScaling);

      IEnumerable<double> y;
      y = ds.GetDoubleValues(TargetVariable, rows);

      int n = x.GetLength(0);

      var columns = Enumerable.Range(0, x.GetLength(1)).ToArray();
      // calculate cholesky decomposed (lower triangular) covariance matrix
      var cov = covarianceFunction.GetParameterizedCovarianceFunction(covarianceParameter, columns);
      this.l = CalculateL(x, cov, sqrSigmaNoise);

      // calculate mean
      var mean = meanFunction.GetParameterizedMeanFunction(meanParameter, columns);
      double[] m = Enumerable.Range(0, x.GetLength(0))
        .Select(r => mean.Mean(x, r))
        .ToArray();

      // calculate sum of diagonal elements for likelihood
      double diagSum = Enumerable.Range(0, n).Select(i => Math.Log(l[i, i])).Sum();

      // solve for alpha
      double[] ym = y.Zip(m, (a, b) => a - b).ToArray();

      int info;
      alglib.densesolverreport denseSolveRep;

      alglib.spdmatrixcholeskysolve(l, n, false, ym, out info, out denseSolveRep, out alpha);
      for (int i = 0; i < alpha.Length; i++)
        alpha[i] = alpha[i] / sqrSigmaNoise;
      negativeLogLikelihood = 0.5 * Util.ScalarProd(ym, alpha) + diagSum + (n / 2.0) * Math.Log(2.0 * Math.PI * sqrSigmaNoise);

      // derivatives
      int nAllowedVariables = x.GetLength(1);

      alglib.matinvreport matInvRep;
      double[,] lCopy = new double[l.GetLength(0), l.GetLength(1)];
      Array.Copy(l, lCopy, lCopy.Length);

      alglib.spdmatrixcholeskyinverse(ref lCopy, n, false, out info, out matInvRep);
      if (info != 1) throw new ArgumentException("Can't invert matrix to calculate gradients.");
      for (int i = 0; i < n; i++) {
        for (int j = 0; j <= i; j++)
          lCopy[i, j] = lCopy[i, j] / sqrSigmaNoise - alpha[i] * alpha[j];
      }

      double noiseGradient = sqrSigmaNoise * Enumerable.Range(0, n).Select(i => lCopy[i, i]).Sum();

      double[] meanGradients = new double[meanFunction.GetNumberOfParameters(nAllowedVariables)];
      for (int k = 0; k < meanGradients.Length; k++) {
        var meanGrad = new double[alpha.Length];
        for (int g = 0; g < meanGrad.Length; g++)
          meanGrad[g] = mean.Gradient(x, g, k);
        meanGradients[k] = -Util.ScalarProd(meanGrad, alpha);
      }

      double[] covGradients = new double[covarianceFunction.GetNumberOfParameters(nAllowedVariables)];
      if (covGradients.Length > 0) {
        for (int i = 0; i < n; i++) {
          for (int j = 0; j < i; j++) {
            var g = cov.CovarianceGradient(x, i, j);
            for (int k = 0; k < covGradients.Length; k++) {
              covGradients[k] += lCopy[i, j] * g[k];
            }
          }

          var gDiag = cov.CovarianceGradient(x, i, i);
          for (int k = 0; k < covGradients.Length; k++) {
            // diag
            covGradients[k] += 0.5 * lCopy[i, i] * gDiag[k];
          }
        }
      }

      hyperparameterGradients =
        meanGradients
        .Concat(covGradients)
        .Concat(new double[] { noiseGradient }).ToArray();

    }