libprima / prima

PRIMA is a package for solving general nonlinear optimization problems without using derivatives. It provides the reference implementation for Powell's derivative-free optimization methods, i.e., COBYLA, UOBYQA, NEWUOA, BOBYQA, and LINCOA. PRIMA means Reference Implementation for Powell's methods with Modernization and Amelioration, P for Powell.
http://libprima.net
BSD 3-Clause "New" or "Revised" License
304 stars 40 forks source link

Warn user if they have provided information that will not be used #134

Closed nbelakovski closed 9 months ago

nbelakovski commented 9 months ago

@zaikunzhang This should be a quick one. There will be tests for these statements added in the Python bindings PR.

zaikunzhang commented 9 months ago

Hi @nbelakovski ! Thank you for proposing this.

I do believe we should raise an error here, not a warning. Obviously, the inputs are wrong. It is not ideal to initiate the algorithm, since the function evaluations are expensive.

For your reference, see

PRIMA/matlab: https://github.com/libprima/prima/blob/v0.7.2/matlab/interfaces/private/preprima.m#L242-L254 PDFO/Python: https://github.com/pdfo/pdfo/blob/v2.0.0/python/pdfo/_common.py#L595-L605

Note the following:

  1. Indeed, we should always refine (preprocess) the problem before sending it to the algorithms. After the refinement, the problem dimension and type may change. It may happen that the "raw" problem does not match the proposed algorithm but the "refined" one does. In that case, ideally, we should continue without raising any warning.

    For example, consider that the user wants to use bobyqa or lincoa to solve the following problem.

    $$\min f(x, y) \quad \text{s.t.} \quad 1\le x\le 1, \sin(x) + y \le 1.$$

    This raw problem is nonlinearly constrained, and hence cannot be solved by the proposed algorithm. However, it can obviously be refined as

    $$\min f(1, y) \quad \text{s.t.} \quad y \le 1 - \sin(1).$$

    This refined problem can be solved by the proposed algorithm.

  2. Obviously, the above-mentioned refinement is too complicated to be done in Fortran or C. They should be done only in Python/MATLAB/Julia/R (see the above-mentioned Python and MATLAB code for your reference), which is not in the scope of this PR. In C, as you proposed in this PR, we should check the raw problem directly without doing any refinement.

  3. PRIMA/matlab provides the following functions to the user: prima, uobyqa, newuoa, bobyqa, lincoa, and cobyla. It is similar for PDFO/Python. This makes the logic of the code much more complicated than providing only prima, which PRIMA/Python should do.

  4. PRIMA/matlab and PDFO/Python are good references for developing PRIMA/Python. Thank you for taking a look at them (not only the signatures but also the code). The behavior of PRIMA/Python should be consistent with them.

Thanks.


P.S.: Who on earth would solve a problem like

$$\min f(1, y) \quad \text{s.t.} \quad 1 \le x \le 1, \sin(x) + y \le 1?$$

Answer:

  1. Mathematically, we cannot forbid users from doing so.
  2. (More important) As a solver developer, we should keep the following facts in mind: a) The problem we solve is likely only a small subproblem in the user's project. b) The user may use our solver to solve a sequence of small subproblems rather than only one. c) The user is not necessarily a human. It is probably another piece of code. Keeping these facts in mind, think about a piece of code that invokes our solver to solve $$\min f(x, y) \quad \text{s.t.} \quad a_n \le x \le b_n, \sin(x)+y \le 1, \quad n = 1, 2, ...$$ where ${a_n}$ and ${b_n}$ both become virtually equal to $1$ for modestly large $n$.
nbelakovski commented 9 months ago

OK, do you think it would be sufficient to change it from if (condition) {fprintf(stderr, "message\n");} to assert(condition && "message\n");?

zaikunzhang commented 9 months ago

Is there any other way to raise an error ?

Assertions are not friendly and they work only if we are debugging (see, e.g., https://en.cppreference.com/w/c/error/assert).

Thank you.

nbelakovski commented 9 months ago

C doesn't have exceptions, so assert is the only option if you want to prevent the program from continuing. I notice that MATLAB's error throws an exception, and in the Python code an exception is raised, so this behavior is consistent with those two.

zaikunzhang commented 9 months ago

C doesn't have exceptions, so assert is the only option if you want to prevent the program from continuing. I notice that MATLAB's error throws an exception, and in the Python code an exception is raised, so this behavior is consistent with those two.

Yes, I recall this after some searching. We should return with an error code, right?

https://stackoverflow.com/questions/2891766/how-can-i-throw-an-exception-in-c

nbelakovski commented 9 months ago

Yes we could do that, what do you want to call the new error code?

zaikunzhang commented 9 months ago

Yes we could do that, what do you want to call the new error code?

What about problem_solver_mismatch or prob_solv_mismatch? You may choose one according to the style of other codes and adjust the upper/lower case accordingly.

Thanks.

nbelakovski commented 9 months ago

@zaikunzhang changes made, please re-review.

zaikunzhang commented 9 months ago

cannot use them

What about "cannot handle them"?

nbelakovski commented 9 months ago

"cannot handle them" sounds like the algorithm is fragile and is likely to do weird things if the extra info is provided, when in fact it won't even get the info at all. So in a sense it can handle them, it just can't do anything with them.

zaikunzhang commented 9 months ago

"cannot handle them" sounds like the algorithm is fragile and is likely to do weird things if the extra info is provided, when in fact it won't even get the info at all. So in a sense it can handle them, it just can't do anything with them.

If I am writing or reading a paper, "NEUWOA cannot handle constraints" sounds much better than "NEWUOA cannot use constraints". The former is quite standard phrasing while the latter does not really make sense to me ...

zaikunzhang commented 9 months ago

"cannot handle them" sounds like the algorithm is fragile and is likely to do weird things if the extra info is provided

The bounds etc are not "extra info", but part of the problem. Once we realize what is the definition of the problem, I guess it will be clear that the algorithm does "do wired things".

A different but related discussion is https://github.com/libprima/prima/pull/102#issuecomment-1779284321 .

when in fact it won't even get the info at all.

This is the implementation, but, mathematically speaking, what really happens is that they cannot understand the info and ignore it (which is a "wired thing" to do). If we did not implement the code so that they do not get the info, then the algorithms will not even understand the problem, let alone solve it.

zaikunzhang commented 9 months ago

Should we worry about the segfault at https://github.com/nbelakovski/prima/actions/runs/7444420673/job/20250795340 ?

nbelakovski commented 9 months ago

OK, I've changed it to "cannot handle them"

Should we worry about the segfault at https://github.com/nbelakovski/prima/actions/runs/7444420673/job/20250795340 ?

It's fixed, it was coming from data.c and if you look at the changes I made to that file it will be clear why it was segfaulting.

zaikunzhang commented 9 months ago

OK, I've changed it to "cannot handle them"

Should we worry about the segfault at https://github.com/nbelakovski/prima/actions/runs/7444420673/job/20250795340 ?

It's fixed, it was coming from data.c and if you look at the changes I made to that file it will be clear why it was segfaulting.

Thank you @nbelakovski . I have not more comments. This can be merged if @jschueller has nothing to add.