Skip to content

Commit

Permalink
Document set_exceptions_enabled and get_exceptions_enabled
Browse files Browse the repository at this point in the history
doc/docs/NLopt_C-plus-plus_Reference.md,
doc/docs/NLopt_Guile_Reference.md,
doc/docs/NLopt_Python_Reference.md (Performing the optimization): Add
"by default" wording to the claim that `optimize()` throws an exception
for negative return codes. (The sentence already links to the
"Exceptions" section that has the details.)
(Exceptions): Document nlopt::opt::set_exceptions_enabled and
nlopt::opt::get_exceptions_enabled, and adapt the remainder of the
section to mention their impact where appropriate.
  • Loading branch information
kkofler committed Dec 20, 2024
1 parent 0ed8272 commit e94cd1f
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 8 deletions.
15 changes: 12 additions & 3 deletions doc/docs/NLopt_C-plus-plus_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ nlopt::result nlopt::opt::optimize(std::vector`<double>` &x, double &opt_f);

On input, `x` is a vector of length `n` (the dimension of the problem from the `nlopt::opt` constructor) giving an initial guess for the optimization parameters. On successful return, `x` contains the optimized values of the optimization parameters, and `opt_f` contains the corresponding value of the objective function.

The return value (see below) is positive on success, indicating the reason for termination. On failure (negative return codes), it throws an exception (see [Exceptions](#exceptions), below).
The return value (see below) is positive on success, indicating the reason for termination. On failure (negative return codes), by default, it throws an exception (see [Exceptions](#exceptions), below).

You can also call the following methods to retrieve the `opt_f` value from the last `optimize` call, and the return value (including negative/failure return values) from the last `optimize` call:

Expand All @@ -294,7 +294,7 @@ The possible return values are the same as the [return values in the C API](NLop
Exceptions
----------

The [Error codes (negative return values)](NLopt_Reference.md#error-codes-negative-return-values) in the C API are replaced in the C++ API by thrown exceptions. The following exceptions are thrown by the various routines:
If exceptions are enabled (the default), the [Error codes (negative return values)](NLopt_Reference.md#error-codes-negative-return-values) in the C API are replaced in the C++ API by thrown exceptions. The following exceptions are thrown by the various routines:

```
std::runtime_error
Expand All @@ -320,7 +320,16 @@ Halted because roundoff errors limited progress, equivalent to `NLOPT_ROUNDOFF_L
`nlopt::forced_stop` (subclass of `std::runtime_error`)
Halted because of a [forced termination](#forced-termination): the user called `nlopt::opt::force_stop()` from the user’s objective function or threw an `nlopt::forced_stop` exception. Equivalent to `NLOPT_FORCED_STOP`.

If your objective/constraint functions throw *any* exception during the execution of `nlopt::opt::optimize`, it will be caught by NLopt and the optimization will be halted gracefully, and `nlopt::opt::optimize` will re-throw an exception. However, the exception that is re-thrown by `nlopt::opt::optimize` will be one of the five exceptions above; if the exception thrown by your code was not one of these five, it will be converted to a generic `std::runtime_error` exception. (The reason for this is that C++ has no clean way to save an arbitrary exception and rethrow it later, outside the original `catch` statement.) Therefore, if you want to do something special in response to a particular exception that is not one of these five, you should catch it yourself in your function, handle it however you want, and re-throw if desired.
Whether this behavior is enabled or whether `nlopt::opt::optimize` just returns the error code as is is controlled by the `enable_exceptions` flag in `nlopt::opt`, which can be set and retrieved with the methods below.

```
void nlopt::opt::set_exceptions_enabled(bool enable);
bool nlopt::opt::get_exceptions_enabled() const;
```

The default is `true`, i.e., to throw an exception. When setting `set_exceptions_enabled(false)`, it is the caller's responsibility to *manually* check the return code, as in C (or, equivalently, the `last_optimize_result`). This is especially useful in the bindings for other programming languages (e.g., Python), where `nlopt::opt::optimize` is modified to return the best point as a return value instead of overwriting the start point, and hence, if an exception is thrown, the best point can no longer be retrieved. But it can also be useful for pure C++ code.

If your objective/constraint functions throw *any* exception during the execution of `nlopt::opt::optimize`, it will be caught by NLopt and the optimization will be halted gracefully, and, if exceptions are enabled, `nlopt::opt::optimize` will re-throw an exception. However, the exception that is re-thrown by `nlopt::opt::optimize` will be one of the five exceptions above; if the exception thrown by your code was not one of these five, it will be converted to a generic `std::runtime_error` exception. (The reason for this is that C++ has no clean way to save an arbitrary exception and rethrow it later, outside the original `catch` statement.) Therefore, if you want to do something special in response to a particular exception that is not one of these five, you should catch it yourself in your function, handle it however you want, and re-throw if desired. If exceptions are disabled, `nlopt::opt::optimize` will return `NLOPT_FORCED_STOP` instead of rethrowing a C++ exception, though bindings for languages such as Python may rethrow, e.g., Python exceptions.

Local/subsidiary optimization algorithm
---------------------------------------
Expand Down
13 changes: 11 additions & 2 deletions doc/docs/NLopt_Guile_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ You can call the following methods to retrieve the optimized objective function
```


The return code (see below) is positive on success, indicating the reason for termination. On failure (negative return codes), `optimize` throws an exception (see [Exceptions](#exceptions), below).
The return code (see below) is positive on success, indicating the reason for termination. On failure (negative return codes), by default, `optimize` throws an exception (see [Exceptions](#exceptions), below).

### Return values

Expand All @@ -239,7 +239,7 @@ The possible return values are the same as the [return values in the C API](NLop
Exceptions
----------

The [Error codes (negative return values)](NLopt_Reference.md#error-codes-negative-return-values) in the C API are replaced in the Guile API by thrown exceptions. The exception key takes the form of a Scheme symbol. The following exception keys are thrown by the various routines:
If exceptions are enabled (the default), the [Error codes (negative return values)](NLopt_Reference.md#error-codes-negative-return-values) in the C API are replaced in the Guile API by thrown exceptions. The exception key takes the form of a Scheme symbol. The following exception keys are thrown by the various routines:

```
runtime-error
Expand All @@ -265,6 +265,15 @@ Halted because roundoff errors limited progress, equivalent to `NLOPT_ROUNDOFF_L
`forced-stop` (subclass of `Exception`)
Halted because of a forced termination: the user called `opt.force_stop()` from the user’s objective function. Equivalent to `NLOPT_FORCED_STOP`.

Whether this behavior is enabled or whether `nlopt-opt-optimize` just returns the error code as is is controlled by the `enable_exceptions` flag in `nlopt::opt`, which can be set and retrieved with the methods below.

```
(nlopt-opt-set_exceptions_enabled opt enable)
(nlopt-opt-get_exceptions_enabled opt)
```

The default is `#t` (true), i.e., to throw an exception. When setting `(nlopt-opt-set_exceptions_enabled opt #f)` (false), it is the caller's responsibility to *manually* check `(nlopt-opt-last-optimize-result opt)`. While that makes the `#f` setting more error-prone, it has the advantage that the best point found (which can be quite good even in some error cases) can still be returned through the return value of `nlopt-opt-optimize`, so is not lost, whereas if exceptions are enabled through `(nlopt-opt-set_exceptions_enabled opt #t)`, the exception prevents the best point from being returned.

Currently, NLopt does not catch any exceptions that you might throw from your objective or constraint functions. (In the future, we might catch these exceptions, halt the optimization gracefully, and then re-throw, as in Python or C++, but this is not yet implemented.) So, throwing an exception in your objective/constraint may result in a memory leak.

To catch an [exception in Guile](http://www.gnu.org/software/guile/manual/html_node/Exceptions.html), you need to define a [throw-handler](http://www.gnu.org/software/guile/manual/html_node/Throw-Handlers.html) function. For example, the following code calls `nlopt-opt-optimize`, catches *any* exception (a key of `#t` in `catch`), and prints out the error return code:
Expand Down
15 changes: 12 additions & 3 deletions doc/docs/NLopt_Python_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ opt_val = opt.last_optimum_value()
result = opt.last_optimize_result()
```

The return code (see below) is positive on success, indicating the reason for termination. On failure (negative return codes), `optimize()` throws an exception (see [Exceptions](#exceptions), below).
The return code (see below) is positive on success, indicating the reason for termination. On failure (negative return codes), by default, `optimize()` throws an exception (see [Exceptions](#exceptions), below).

### Return values

Expand All @@ -277,7 +277,7 @@ The possible return values are the same as the [return values in the C API](NLop
Exceptions
----------

The [Error codes (negative return values)](NLopt_Reference.md#error-codes-negative-return-values) in the C API are replaced in the Python API by thrown exceptions. The following exceptions are thrown by the various routines:
If exceptions are enabled (the default), the [Error codes (negative return values)](NLopt_Reference.md#error-codes-negative-return-values) in the C API are replaced in the Python API by thrown exceptions. The following exceptions are thrown by the various routines:

```
RunTimeError
Expand All @@ -303,7 +303,16 @@ Halted because roundoff errors limited progress, equivalent to `NLOPT_ROUNDOFF_L
`nlopt.ForcedStop` (subclass of `Exception`)
Halted because of a [forced termination](#forced-termination): the user called `opt.force_stop()` from the user’s objective function or threw an `nlopt.ForcedStop` exception. Equivalent to `NLOPT_FORCED_STOP`.

If your objective/constraint functions throw *any* exception during the execution of `opt.optimize`, it will be caught by NLopt and the optimization will be halted gracefully, and `opt.optimize` will re-throw the *same* exception to its caller.
Whether this behavior is enabled or whether `optimize` just returns the error code as is is controlled by the `enable_exceptions` flag in `nlopt.opt`, which can be set and retrieved with the methods below.

```
opt.set_exceptions_enabled(enable)
opt.get_exceptions_enabled()
```

The default is `True`, i.e., to throw an exception. When setting `opt.set_exceptions_enabled(False)`, it is the caller's responsibility to *manually* check `opt.last_optimize_result()`. While that makes the `False` setting more error-prone, it has the advantage that the best point found (which can be quite good even in some error cases) can still be returned through the return value of `optimize`, so is not lost, whereas if exceptions are enabled through `opt.set_exceptions_enabled(True)`, the exception prevents the best point from being returned.

If your objective/constraint functions throw *any* exception during the execution of `opt.optimize`, it will be caught by NLopt and the optimization will be halted gracefully, and `opt.optimize` will re-throw the *same* exception to its caller. For Python, the exception *will always* be rethrown, even if exceptions are otherwise disabled (`opt.set_exceptions_enabled(False)`).

Local/subsidiary optimization algorithm
---------------------------------------
Expand Down

0 comments on commit e94cd1f

Please sign in to comment.