mlpack / ensmallen

A header-only C++ library for numerical optimization --
http://ensmallen.org
Other
740 stars 119 forks source link

Bounds for search space #364

Closed Kaltxi closed 1 year ago

Kaltxi commented 1 year ago

Hello! First of all, thanks for your work, really like the library!

I have optimization problems of arbitrary functions which could have many minima, but only the ones in a particular part of the variable space are of interest. Currently the best approach I could find was to evaluate the function to std::numerical_limits::max() when inputs are outside the bounds, which certainly made the results better. Is this the idiomatic way? If not, is there a better approach or maybe plans to add bounds as parameters to the optimizer?

zoq commented 1 year ago

Hello, depending on the optimizer it's possible to set the bounds, see https://ensmallen.org/docs.html#pso for an example. What optimizer are you using?

rcurtin commented 1 year ago

@zoq is right, there are some techniques that specifically support constrained optimization. If you still want to use a differentiable optimizer like SGD or L-BFGS or something that doesn't specifically have supports for constraints, I would suggest a "soft" constraint instead of simply evaluating to DBL_MAX (or similar); think, e.g., adding some constant multiplied by how far you are from the edge of the bounds. So for a one-dimensional problem, if you want something in [0, 1], maybe add, say, 1000 * (std::min(x[0], x[0] - 1)) or something like this if x[0] is not in [0, 1]. Just an idea.

Kaltxi commented 1 year ago

@zoq Sorry for late reply! I was using DE. PSO for me has a strange bug of not terminating even due to maxIterations - it just goes on and on; I've looked at the ensmallen code, and it looks like it should work...

@rcurtin thanks for the soft constraint idea, not sure if it is useful for non-differentiable optimizers, my testing for DE seems to yield the same results as with DBL_MAX:

auto outOfBoundsLoss(const arma::mat& coordinates,
                     const arma::mat& lower,
                     const arma::mat& upper) -> std::optional<double> {
  constexpr double PENALTY = 1000.0;
  std::optional<double> loss{};

  for (int i = 0; i < coordinates.size(); ++i) {
    if (coordinates[i] < lower[i]) {
      loss = PENALTY * (lower[i] - coordinates[i]) + loss.value_or(0.0);
    } else if (coordinates[i] > upper[i]) {
      loss = PENALTY * (coordinates[i] - upper[i]) + loss.value_or(0.0);
    }
  }

  return loss;
}

auto ObjectiveType::Evaluate(const arma::mat coordinates) -> double {
  const auto outOfBounds =
      outOfBoundsLoss(aberrations, {-100, -10'000}, {100, 10'000});

  if (outOfBounds.has_value()) {
    return outOfBounds.value();
  }

  // In-bounds calculations
}
Kaltxi commented 1 year ago

Ok, there is no problem with maxIterations, I was counting evaluations, not iterations... But bounds for PSO do not work, I still need to use the loss-bounding function. It kinda makes sense, even in documentation it is written that bounds are only for the initial population.

Also, a better approach with the above code would be not to return the out-of-bounds loss, but instead add it as penalty, otherwise algorithm can optimize out-of-bounds and converge to one of the bounds:

auto ObjectiveType::Evaluate(const arma::mat coordinates) -> double {
  const auto penalty =
      outOfBoundsLoss(aberrations, {-100, -10'000}, {100, 10'000});

  // In-bounds calculations
  // result = ...

  return result + penalty.value_or(0.0);
}
mlpack-bot[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions! :+1: