@Override
  public void updateEstimate(DiscreteMarginalValues<MultiballVariableState> observedValues) {
    iteration++;
    iteration %= iterationSkips;
    // turn variableDomain into an array of X values

    Set<MultiballVariableState> states = observedValues.getValues().keySet();
    xVals = new double[states.size()];
    stateArray = new MultiballVariableState[xVals.length];

    int i = 0;
    for (MultiballVariableState state : states) {
      xVals[i] = state.getValue();
      stateArray[i] = state;
      i++;
    }
    // turn marginalFunction into an array of Y values and t values

    yVals = new double[xVals.length];
    tVals = new double[xVals.length];

    for (i = 0; i < xVals.length; i++) {
      yVals[i] = observedValues.getValue(stateArray[i]);
      tVals[i] = getAndIncrementTVal(stateArray[i]);
    }

    memory.add(xVals, yVals, tVals);

    if ((iteration % iterationSkips) != 0) return;

    Matrix transform = HybridGPMSTransform.generate(memory.iterationNum);

    Matrix trainY = transform.times(memory.Y);

    // create Gaussian process Bayesian Markov chain to estimate worth of setting
    // states to new values

    GaussianProcessRegressionBMC regression = new GaussianProcessRegressionBMC();
    regression.setCovarianceFunction(
        new HybridGPMSCovarianceFunction(
            SquaredExponentialCovarianceFunction.getInstance(), transform, noise));

    BasicPrior[] priors = new BasicPrior[priorMeans.length];
    for (i = 0; i < priorMeans.length; i++) {
      priors[i] = new BasicPrior(priorSampleCounts[i], priorMeans[i], priorStandardDevs[i]);
    }

    regression.setPriors(priors);

    predictor = regression.calculateRegression(memory.X, trainY);

    worstCurrentVal = Double.POSITIVE_INFINITY;

    for (i = 0; i < xVals.length; i++) {
      double worth = evaluate(xVals[i], tVals[i] + 1);
      if (worth < worstCurrentVal) {
        worstCurrentVal = worth;
        worstCurrentIndex = i;
      }
    }

    bestSoFar = worstCurrentVal;
  }