@Override
  public double[] getLogLikelihoods(List<Beacon> beacons, State[] locations) {
    List<Beacon> beaconsCleansed = Beacon.filterBeacons(beacons, minRssi, maxRssi);

    beaconsCleansed = beaconFilter.setBLEBeacon(bleBeacons).filter(beaconsCleansed, locations);
    BLEBeacon.setBLEBeaconIdsToMeasuredBeacons(bleBeacons, beaconsCleansed);
    int[] activeBeaconList = ModelAdaptUtils.beaconsToActiveBeaconArray(beaconsCleansed);

    final double[] ySub = ModelAdaptUtils.beaconsToVecSubset(beaconsCleansed);
    final double[][] X = ModelAdaptUtils.locationsToMat(locations);

    // Adjust bias by average bias
    final double[] rssiBiases = ModelAdaptUtils.biasesToVec(locations);

    double logLLs[] = null;
    try {
      Class<?> cls = gpLDPL.getClass();
      Constructor<?> cst = cls.getConstructor(gpLDPL.getClass());
      final GaussianProcessLDPLMean gpLDPLtmp = (GaussianProcessLDPLMean) cst.newInstance(gpLDPL);
      gpLDPLtmp.updateByActiveBeaconList(activeBeaconList);
      int n = X.length;
      final double logpro[] = new double[n];
      Future<?>[] futures = new Future<?>[n];
      for (int i = 0; i < n; i++) {
        final int idx = i;
        futures[idx] =
            ExecutorServiceHolder.getExecutorService()
                .submit(
                    new Runnable() {
                      public void run() {
                        // Subtract bias from an observation vector.
                        double[] ySubAdjusted = ArrayUtils.addScalar(ySub, -rssiBiases[idx]);
                        logpro[idx] = gpLDPLtmp.logProbaygivenx(X[idx], ySubAdjusted);
                      }
                    });
      }
      for (int i = 0; i < n; i++) {
        futures[i].get();
      }
      logLLs = logpro;
    } catch (InstantiationException
        | IllegalAccessException
        | IllegalArgumentException
        | InvocationTargetException
        | NoSuchMethodException
        | SecurityException e) {
      e.printStackTrace();
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
    return logLLs;
  }
  protected void trainByAveragedSamples(List<Sample> samples) {
    int nBeacons = bleBeacons.size();
    BLEBeacon.setBLEBeaconIdsToSamples(bleBeacons, samples);
    setupGPLDPL();
    this.samples = samples;
    double[][] X = ModelAdaptUtils.samplesToLocationsMat(samples);
    double[][] Y = ModelAdaptUtils.samplesToBeaconsListMat(samples, minRssi, nBeacons);
    double[][] bLocs = ModelAdaptUtils.BLEBeaconsToMat(bleBeacons, nBeacons);
    gpLDPL.setSourceLocs(bLocs);

    int[] actBecList = new int[nBeacons];
    for (int i = 0; i < nBeacons; i++) actBecList[i] = i;
    gpLDPL.setActiveBeaconList(actBecList);

    gpLDPL.fit(X, Y);
  }
  public List<Beacon> predictBeacons(State state, List<Beacon> beacons) {
    double[] x = ModelAdaptUtils.locationToVec(state);
    int[] activeBeaconList = ModelAdaptUtils.beaconsToActiveBeaconArray(beacons);
    double[] ypred;
    synchronized (gpLDPL) {
      gpLDPL.updateByActiveBeaconList(activeBeaconList);
      ypred = gpLDPL.predictygivenx(x);
    }

    List<Beacon> beaconsPred = new ArrayList<Beacon>();
    for (int i = 0; i < activeBeaconList.length; i++) {
      int major = -1;
      int minor = activeBeaconList[i];
      double rssi = ypred[i];
      Beacon beacon = new Beacon(major, minor, (float) rssi);
      beaconsPred.add(beacon);
    }

    return beaconsPred;
  }
  protected void trainWithHyperParameters(List<List<Sample>> samplesListTrainParent) {
    List<Sample> samplesAvaragedTrain = Sample.meanTrimList(samplesListTrainParent, minRssi);
    this.train(samplesAvaragedTrain);
    // Update active beacon list about actually observed beacons
    Sample samplesAllAverage = Sample.mean(samplesAvaragedTrain, minRssi);
    List<Beacon> observedBeacons = samplesAllAverage.getBeacons();
    this.setActiveBeaconList(ModelAdaptUtils.beaconsToActiveBeaconArray(observedBeacons));

    double stdevTmp = stdev;
    this.setStdev(0.0); // stdev is set to be zero before optimization of LDPL model
    this.optimizeForLDPLModel();

    this.setStdev(stdevTmp); // stdev must be updated before optimization of GP
    this.optimizeForGPIsoScaleLOOMSE(); // optimize for lx(=ly) and stdev
    this.optimizeEstimateSigmaN(
        Sample.samplesListToSamples(samplesListTrainParent)); // estimate sigmaN

    this.train(samplesAvaragedTrain);
  }