<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 <div style="clear
Step1:  Basics
Easiest way to optimize a function is to pass it as a lambda function to DE and call solve. For example, find the minimum of the function $f = \sum_i x_i^2 + 1$ in 5 dimensions ($x = [x_0,\dots,x_4]$), bounding the search between -10 and 10 for each $x_i$
Step2:  In many scenarios, the function to optimize may depend on many other components or other fixed parameters. It is very convenient to define a function to create your optimizable function. Here is an example of this, where create_loss is a function that prepares the data and returns another function to be optimize
Step3:  You can control also the search process. Use geniterator() to get an iterator at the level of the population (by default, .iterator() returns an iterator at the level of individual (thus it iteraters maxiters * popsize  times)
<ASSISTANT_TASK:>
Python Code:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import sys
# Load Yabox (from local)
# Comment this line to use the installed version
sys.path.insert(0, '../')
import yabox as yb
# Import the DE implementations
from yabox.algorithms import DE, PDE
print('Yabox version: ', yb.__version__)
Explanation: <div style="clear: both; width: 100%; overflow: auto"><img src="img/yabox.png" style="width: 250px; float: left"/></div>
Yabox: Yet another black-box optimization library for Python - https://github.com/pablormier/yabox
Tutorial of the basic functionality of Yabox
Author: Pablo Rodríguez-Mier, @pablormier
Imports
End of explanation
DE(lambda x: sum(x**2) + 1, [(-10, 10)] * 5, maxiters=10000).solve(show_progress=True)
Explanation: Basics
Easiest way to optimize a function is to pass it as a lambda function to DE and call solve. For example, find the minimum of the function $f = \sum_i x_i^2 + 1$ in 5 dimensions ($x = [x_0,\dots,x_4]$), bounding the search between -10 and 10 for each $x_i$:
End of explanation
def create_loss(p1, p2, p3):
    # Prepare here whatever you need. Load parameters,
    # read from file, etc
    a = p1 * p2
    b = p2 / p3
    # Define the function to be optimized as an inner function
    # that can make use of the other parameters
    def f(x):
        return 1 + a*x - b*x**2 + 0.01*x**3 + 0.001 * x**4
    return f
f = create_loss(5, 2, 0.1)
f(0)
x = np.arange(-150, 150, 0.1)
plt.plot(x, f(x));
xo, yo = DE(f, [(-150, 150)], maxiters=1000).solve(show_progress=True)
xo, yo
fig, ax = plt.subplots()
ax.plot(x, f(x));
ax.plot(xo[0][0], yo[0], '*')
Explanation: In many scenarios, the function to optimize may depend on many other components or other fixed parameters. It is very convenient to define a function to create your optimizable function. Here is an example of this, where create_loss is a function that prepares the data and returns another function to be optimize:
End of explanation
# Control the iteration process
de = DE(f, [(-150, 150)], maxiters=30)
for step in de.geniterator():
    idx = step.best_idx
    norm_vector = step.population[idx]
    best_params = de.denormalize([norm_vector])
    print(step.best_fitness, norm_vector, best_params[0])
Explanation: You can control also the search process. Use geniterator() to get an iterator at the level of the population (by default, .iterator() returns an iterator at the level of individual (thus it iteraters maxiters * popsize  times)
End of explanation
<END_TASK>