@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;
  }
  public MarginalValues<?> calculateMarginalMaxFunction(
      Map<Variable<?, ?>, MarginalValues<?>> sortedMessages, Variable<?, ?> variable) {

    DiscreteVariable<?> destinationVariable = (DiscreteVariable<?>) variable;

    DiscreteMarginalValues<?> values = new DiscreteMarginalValues();

    for (DiscreteVariableState state : destinationVariable.getDomain()) {
      values.put(state, findMaxValue(destinationVariable, state, sortedMessages));
    }

    for (DiscreteVariable<?> var : function.getVariableExpansionOrder()) {
      if (!sortedMessages.containsKey(var)) continue;

      DiscreteVariable var2 = (DiscreteVariable) function.getVariableDependency(var);

      // if (var2 == null) {
      // System.out.println(function.getVariableExpansionOrder());
      // System.out.println(var);
      // System.out.println(function.getVariableDependencies());
      // }
      //
      // if (!var.getDomain().equals(var2.getDomain())) {
      // System.out.println(var.getDomain());
      // System.out.println(var2.getDomain());
      // throw new IllegalArgumentException();
      // }

      DiscreteMarginalValues<?> marginalValues =
          (DiscreteMarginalValues<?>) sortedMessages.get(var);

      if (marginalValues == null) {
        System.out.println(sortedMessages);
        System.out.println(var);
        throw new IllegalArgumentException();
      }

      // if
      // (!marginalValues.getValues().keySet().equals(var.getDomain().getStates()))
      // {
      // System.out.println(marginalValues);
      // System.out.println(var.getDomain());
      // throw new IllegalArgumentException();
      // }

    }

    if (validate) {
      DiscreteMarginalValues<?> expectedValues =
          (DiscreteMarginalValues<?>)
              groundTruth.calculateMarginalMaxFunction(sortedMessages, variable);

      boolean error = false;

      for (DiscreteVariableState state : values.getValues().keySet()) {
        if (Math.abs(values.getValues().get(state) - expectedValues.getValues().get(state))
            > 1e-10) {
          error = true;
          break;
        }
      }

      if (error) {
        System.out.println(
            "Value from OptimisedMarginalMaximisation is not equal to BBDiscreteMarginalMaximisation");
        System.out.println(sortedMessages);

        System.out.println("Expected: " + expectedValues);
        System.out.println("Got " + values);

        for (DiscreteVariableState state : values.getValues().keySet()) {
          System.out.println(state);
          System.out.println(values.getValues().get(state));
          System.out.println(expectedValues.getValues().get(state));
        }

        throw new IllegalArgumentException();
      }
    }

    return values;
  }