This provides the GOSOLNP optimizaiton method.
Project description
pygosolnp - Random initialization and multiple restarts using pysolnp
See full documentation on http://solnp.readthedocs.io.
Description
GOSOLNP tries to find the optimum for the general nonlinear optimization problem on the form:
minimize f(x)
subject to
g(x) = e_x
l_h <= h(x) <= u_h
l_x < x < u_X
where f(x), g(x) and h(x) are smooth functions.
GOSOLNP tries to find the global optimum for given problem as explained below:
- Generate
nrandom starting parameters based on some specified distribution and evaluate them (lower value is better) based on one of two evaluation functions::- Objective function
f(x)for allxthat satisfies the inequalit constraintl_h <= h(x) <= u_h - Penalty function:
f(x) + 100 * sum(max(0, 0.9 + l_x - g(x))^2 + max(0, 0.9 + g(x) - u_x)^2) + sum(h(x) - e_x)^2/100
- Objective function
- For the
mstarting parameters with the lowest evaluation function value, run pysolnp to find nearest optimum. - Return the best valid solution among the ones found through the various starting parameters (lowest solution value within bounds)
Compatability
Python source code written to be compatible with Python 3.6+.
Depends on the pysolnp library.
Note: pysolnp is available on pip but for best results building pysolnp from source is recommended, as BLAS and LAPACK will make a difference.
Installation
Simply install the package through PyPi with:
pip install pygosolnp
Usage
Below is the Electron example, for the complete example see /python_examples/example_electron.py.
import pygosolnp
from math import sqrt
number_of_charges = 25
def obj_func(data):
x = data[0:number_of_charges]
y = data[number_of_charges:2 * number_of_charges]
z = data[2 * number_of_charges:3 * number_of_charges]
result = 0.0
for i in range(0, number_of_charges - 1):
for j in range(i + 1, number_of_charges):
result += 1.0 / sqrt((x[i] - x[j]) ** 2 + (y[i] - y[j]) ** 2 + (z[i] - z[j]) ** 2)
return result
def eq_func(data):
x = data[0:number_of_charges]
y = data[number_of_charges:2 * number_of_charges]
z = data[2 * number_of_charges:3 * number_of_charges]
result = [None] * number_of_charges
for i in range(0, number_of_charges):
result[i] = x[i] ** 2 + y[i] ** 2 + z[i] ** 2
return result
parameter_lower_bounds = [-1] * number_of_charges * 3
parameter_upper_bounds = [1] * number_of_charges * 3
equality_constraints = [1] * number_of_charges
results = pygosolnp.solve(
obj_func=obj_func,
eq_func=eq_func,
eq_values=equality_constraints,
par_lower_limit=parameter_lower_bounds,
par_upper_limit=parameter_upper_bounds,
number_of_simulations=20000, # This represents the number of starting guesses to use
evaluation_type=pygosolnp.EvaluationType.OBJECTIVE_FUNC_EXCLUDE_INEQ, # This specifies how starting guesses are evaluated
number_of_restarts=4, # This specifies how many restarts to run from the best starting guesses
number_of_processes=None, # None here means to run everything single-processed
seed=443, # Seed for reproducibility, if omitted the default random seed is used (typically cpu clock based)
pysolnp_max_major_iter=100, # Pysolnp property
debug=False)
all_results = results.all_results
print("; ".join([f"Solution {index + 1}: {solution.obj_value}" for index, solution in enumerate(all_results)]))
best_solution = results.best_solution
print(f"Best solution {best_solution.obj_value} for parameters {best_solution.parameters}.")
Output:
Solution 1: 244.1550118432253; Solution 2: 243.9490050190484; Solution 3: 185.78533081425041; Solution 4: 244.07921194485854
Best solution 243.9490050190484 for parameters [0.34027682232302764, 0.6883848066130182, 0.40606935432390506, -0.48792021292031806, -0.9178828953524689, -0.8589108634903266, 0.5283358549116118, 0.5728961925249723, 0.050290270369804546, 0.2822196996653568, -0.28946049710390886, 0.9330667664325792, -0.417772874000437, -0.03124740841970295, -0.29956912974747735, -0.10795596769157587, 0.3549207051381202, -0.8488364868994906, -0.6188824315686104, 0.8670714826307561, 0.3619506513550691, -0.8251195998826993, 0.8981487824398298, -0.3070816517072349, -0.2904911409773652, -0.35929970112105275, 0.38265416704984406, 0.33719255494620365, 0.650145631465414, -0.009286462818493796, -0.465918386592264, -0.8033631014752087, -0.7401045643478271, 0.3960071831167597, 0.8935914355529017, -0.06721625611418029, -0.17225237197258644, -0.15098042850508767, -0.7478725125678873, -0.6812276561168169, -0.9904585930824136, 0.856850019939644, 0.5254197207147568, -0.7013999163528392, 0.48657417413232107, -0.26408411581924884, 0.07567634864162288, -0.11182932375860347, 0.6420701581875298, 0.9557533156823153, 0.8689739558884132, -0.6161997929208027, 0.8493580035603775, -0.5824473351785862, -0.396742846647288, 0.21258614114742239, -0.27471641138639197, 0.3521864803783331, -0.9168692752182334, -0.3490651393423451, 0.9548271150556085, -0.3157777175001705, -0.8959190430352647, -0.6631065760657502, 0.667972799116207, 0.08565844771052915, 0.3739510687437103, 0.058402195224914855, -0.3535858335621826, 0.10692372605836734, -0.8940086985713928, 0.5598670317503106, 0.4252328966724537, 0.7024576680631351, 0.04637407503369189].
Parameters
The basic signature is:
pygosolnp.solve(
obj_func: Callable,
par_lower_limit: List[float],
par_upper_limit: List[float],
eq_func: Optional[Callable] = None,
eq_values: Optional[List[float]] = None,
ineq_func: Optional[Callable] = None,
ineq_lower_bounds: Optional[List[float]] = None,
ineq_upper_bounds: Optional[List[float]] = None,
number_of_restarts: int = 1,
number_of_simulations: int = 20000,
number_of_processes: Optional[int] = None,
start_guess_sampling: Union[None, List[Distribution], Sampling] = None,
seed: Union[None, int] = None,
evaluation_type: Union[EvaluationType, int] = EvaluationType.OBJECTIVE_FUNC_EXCLUDE_INEQ,
pysolnp_rho: float = 1.0,
pysolnp_max_major_iter: int = 10,
pysolnp_max_minor_iter: int = 10,
pysolnp_delta: float = 1e-05,
pysolnp_tolerance: float = 0.0001,
debug: bool = False) -> Results
Inputs:
| Parameter | Type | Default value* | Description |
|---|---|---|---|
| obj_func | Callable[List[float]] | - | The objective function f(x) to minimize. |
| par_lower_limit | List[float] | - | The parameter lower limit x_l. |
| par_upper_limit | List[float] | - | The parameter upper limit x_u. |
| eq_func | Callable[List[float]] | None | The equality constraint function h(x). |
| eq_values | List[float] | None | The equality constraint values e_x. |
| ineq_func | Callable[List[float]] | None | The inequality constraint function g(x). |
| ineq_lower_bounds | List[float] | None | The inequality constraint lower limit g_l. |
| ineq_upper_bounds | List[float] | None | The inequality constraint upper limit g_l. |
| number_of_restarts | int | 1 | The number_of_restarts best evaluation results are used to run pysolnp number_of_restarts times. |
| number_of_simulations | int | 20000 | Sets how many randomly generated starting guesses we generate and evaluate with the evaluation function. |
| number_of_processes | int | None | Sets how many parallel processes to run when solving the problem. If None the problem is solved in the main processes. |
| start_guess_sampling | List[Distribution] or Sampling | None | A list of distributions for generating starting values, one distribution for each parameter. If None, the Uniform distribution is used.*** |
| seed | int | None | By default the MT19937 Generator is used with timestamp-seed. Optionally an integer seed can be supplied. |
| evaluation_type | EvaluationType or int | EvaluationType.OBJECTIVE_FUNC_EXCLUDE_INEQ | Selects the evaluation type from the pygosolnp.EvaluationType enum. |
| pysolnp_rho | float | 1.0 | pysolnp parameter: Penalty weighting scalar for infeasability in the augmented objective function.** |
| pysolnp_max_major_iter | int | 400 | pysolnp parameter: Maximum number of outer iterations. |
| pysolnp_max_minor_iter | int | 800 | pysolnp parameter: Maximum number of inner iterations. |
| pysolnp_delta | float | 1e-07 | pysolnp parameter: Step-size for forward differentiation. |
| pysolnp_tolerance | float | 1e-08 | pysolnp parameter: Relative tolerance on optimality. |
| debug | bool | False | If set to true some debug output will be printed. |
*Defaults for configuration parameters are based on the defaults for Rsolnp.
**Higher values means the solution will bring the solution into the feasible region with higher weight. Very high values might lead to numerical ill conditioning or slow down convergence.
***Supply an instance of a class that inherits the abstract class pygosolnp.sampling.Sampling to provide starting guesses, see below for examples:
- /python_examples/example_grid_sampling.py - Uses Scikit-optimize to generate grid-style random starting guesses.
- /python_examples/example_truncated_normal.py - Uses Scipy random to generate Truncated Normal random numbers using the PCG64 generator.
Output:
The function returns the pygosolnp.Results with the below properties.
| Property | Type | Description |
|---|---|---|
| best_solution | Optional[Result] | The best local optimum found for the problem. |
| all_results | List[Result] | All restarts and their corresponding local optimum. |
| starting_guesses | List[float] | All the randomized starting parameters. |
Each named tuple pygosolnp.Result has the below properties.
| Property | Type | Description |
|---|---|---|
| obj_value | float | The value of the objective function at local optimum f(x*). |
| parameters | List[float] | A list of parameters for the local optimum x*. |
| converged | bool | Boolean which indicates if the solution is within bounds. |
Multiprocessing
pygosolnp supports multi-processing (not multi-threading!) using the standard multi-processing library. This is an advanced feature, please read up on this before using it!
There are various things to consider in order to get time- and memory-efficient executions:
- Multiprocessing will spawn processes, this consumes time and memory, if your problem is small then run it single-threaded!
- Your operating system, notably Linux works better with multiprocessing than Windows.
- All function must be picklable (for example global functions, local lambdas will not work)
Authors
- Krister S Jakobsson - Implementation - krister.s.jakobsson@gmail.com
License
This project is licensed under the Boost License - see the license file for details.
Acknowledgments
- Yinyu Ye - Publisher and mastermind behind the original SOLNP algorithm, Original Sources
- Alexios Ghalanos and Stefan Theussl - The people behind RSOLNP and GOSOLNP,
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pygosolnp-2021.5.1.tar.gz.
File metadata
- Download URL: pygosolnp-2021.5.1.tar.gz
- Upload date:
- Size: 27.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.7.0 requests/2.25.1 setuptools/51.0.0 requests-toolbelt/0.9.1 tqdm/4.60.0 CPython/3.8.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
66f02c2bc819e91f74c725efa97fb2f1db74f3ba3758b042256f470ae799491c
|
|
| MD5 |
da46e51a382a995858ea5e0585f346e0
|
|
| BLAKE2b-256 |
caddbfbb5fd0a57f51833adcd707810627ecae4e083bb8c0910237237e4cc971
|
File details
Details for the file pygosolnp-2021.5.1-py3-none-any.whl.
File metadata
- Download URL: pygosolnp-2021.5.1-py3-none-any.whl
- Upload date:
- Size: 34.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.7.0 requests/2.25.1 setuptools/51.0.0 requests-toolbelt/0.9.1 tqdm/4.60.0 CPython/3.8.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
52a3b8072a4634555396373ed2be108f0adc1a9c7facdb5b18912ee8530fa4dc
|
|
| MD5 |
52b240a38f056b639f2a9245ef5e6e35
|
|
| BLAKE2b-256 |
209b96edca47c3e42c3821884b4253da3e7072846abc612fdda1d41ac9916022
|