Particle Swarm optimization

Particle Swarm optimization#

Example of particle swarm implementation based on PySwarms. This code demonstrates how to use the Particle Swarm Optimization (PSO) algorithm from the PySwarms library to optimize a function. The objective function used in this example is taken from the Ray Tune generic black-box optimizer.

PySwarms: https://pyswarms.readthedocs.io/en/latest/ To install PySwarms, use the following command from console:

pip install pyswarms

You can optimise a mmi1x2 component for a transmission of \(|S_{21}|^2 = 0.5\) (50% power) for a given wavelength using MEEP.

from functools import partial

import gdsfactory as gf
import numpy as np
import pyswarms as ps
from gdsfactory.config import PATH
from gdsfactory.generic_tech import get_generic_pdk

import gplugins.gmeep as gm

# Set up GDSFactory and PDK
gf.config.rich_output()
PDK = get_generic_pdk()
PDK.activate()

# Create a working directory for the PSO optimization
wrk_dir = PATH.cwd / "extra"
wrk_dir.mkdir(exist_ok=True)


# ## Define the loss function used in the PSO optimization
def loss_S21_L1(x, target):
    r"""Loss function. Returns :math:`$\sum_i L_1(x_i)$` and :math:`$x$` as a tuple"""
    return np.abs(target - x), x


# ## Define the trainable function for the PSO optimization
def trainable_simulations(x, loss=lambda x: x):
    """Training step, or `trainable`, function for Ray Tune to run simulations and return results."""
    loss_arr = []
    use_mpi = False

    for xi in x:
        # Component to optimize
        component = gf.components.mmi1x2(length_mmi=xi[0], width_mmi=xi[1])

        # Simulate and get output
        meep_params = dict(
            component=component,
            run=True,
            dirpath=wrk_dir,
            wavelength_start=1.5,
            wavelength_points=1,
            is_3d=False,
        )
        if use_mpi:  # change this to false if no MPI support
            s_params = gm.write_sparameters_meep_mpi(
                cores=2,
                **meep_params,  # set this to be the same as in `tune.Tuner`
            )
            s_params = np.load(
                s_params
            )  # parallel version returns the filepath to npz instead
        else:
            s_params = gm.write_sparameters_meep(**meep_params)

        s_params_abs = np.abs(s_params["o2@0,o1@0"]) ** 2

        loss_x, x = loss(s_params_abs)
        if not np.isscalar(x):  # for many wavelengths, consider sum and mean
            loss_x, x = -loss_x.sum(), x.mean()

        loss_arr.append(loss_x)

    return np.asarray(loss_arr)


# Define the target value for the loss function
loss = partial(loss_S21_L1, target=0.5)
func = partial(trainable_simulations, loss=loss)

# Create bounds for the optimization
max_bound = np.array([0.05, 0.05])
min_bound = np.array([2, 2])
bounds = (min_bound, max_bound)

# Set options for the PSO optimizer
options = {"c1": 0.5, "c2": 0.3, "w": 0.9}

# Create an instance of the PSO optimizer
optimizer = ps.single.GlobalBestPSO(
    n_particles=10, dimensions=2, options=options, bounds=bounds
)
Using MPI version 4.1, 1 processes
2024-08-16 06:17:32.350 | INFO     | gplugins.gmeep:<module>:39 - Meep '1.29.0' installed at ['/home/runner/micromamba/lib/python3.11/site-packages/meep']
# Perform the optimization. We run only comment these lines for demo purposes
# cost, pos = optimizer.optimize(func, iters=100)
# plot_cost_history(cost_history=optimizer.cost_history)
# plt.show()