Optimization change number of processes

This example shows how to set the number of processes used for optimization. The exact same scenario from Tutorial 5 is used:

The objective is to minimize the energy required by a LIFE electrode to trigger a single myelinated fiber using a rectangular pulse stimulus.

Note

For optimization over parallelizable context (nerve or fascicle), the parallelization is done only for the context simulation

See also

Parallel computation and Optimization users’ guides.

[1]:
import sys
sys.path.append("../../../")     #data path
import nrv

import matplotlib.pyplot as plt
import numpy as np
import os

N_test = "227"
figdir = f"./unitary_tests/figures/{N_test}_"

res_dir = f"./Example_/{N_test}"

# -------------------------- #
#  Cost function definition  #
# -------------------------- #
my_cost0 = nrv.cost_function()


# Setting Static Context
axon_file = res_dir + "myelinated_axon.json"

ax_l = 10000 # um
ax_d=10
ax_y=50
ax_z=0
axon_1 = nrv.myelinated(L=ax_l, d=ax_d, y=ax_y, z=ax_z)


LIFE_stim0 = nrv.FEM_stimulation()
LIFE_stim0.reshape_nerve(Length=ax_l)
life_d = 25 # um
life_length = 1000 # um
life_x_0_offset = life_length/2
life_y_c_0 = 0
life_z_c_0 = 0
elec_0 = nrv.LIFE_electrode("LIFE", life_d, life_length, life_x_0_offset, life_y_c_0, life_z_c_0)

dummy_stim = nrv.stimulus()
dummy_stim.pulse(0, 0.1, 1)
LIFE_stim0.add_electrode(elec_0, dummy_stim)

axon_1.attach_extracellular_stimulation(LIFE_stim0)
axon_1.get_electrodes_footprints_on_axon()
axon_dict = axon_1.save(save=False, fname=axon_file, extracel_context=True)

fig, ax = plt.subplots(1, 1, figsize=(6,6))
axon_1.plot(ax)
ax.set_xlim((-1.2*ax_y, 1.2*ax_y))
ax.set_ylim((-1.2*ax_y, 1.2*ax_y))

del axon_1

static_context = axon_dict
t_sim = 5
dt = 0.005
kwarg_sim = {
    "dt":dt,
    "t_sim":t_sim,
}

my_cost0.set_static_context(static_context, **kwarg_sim)

# Setting Context Modifier
t_start = 1
I_max_abs = 100
cm_0 = nrv.biphasic_stimulus_CM(start=t_start, s_cathod="0", t_cathod="1", s_anod=0)
my_cost0.set_context_modifier(cm_0)

# Setting Cost Evaluation
costR = nrv.recrutement_count_CE(reverse=True)
costC = nrv.stim_energy_CE()
cost_evaluation = costR + 0.01 * costC
my_cost0.set_cost_evaluation(cost_evaluation)


# -------------------------- #
#  PSO Optimizer definition  #
# -------------------------- #
pso_kwargs = {
    "maxiter" : 50,
    "n_particles" : 20,
    "opt_type" : "local",
    "options": {'c1': 0.6, 'c2': 0.6, 'w': 0.8, 'k': 3, 'p': 1},
    "bh_strategy": "reflective",
}
pso_opt = nrv.PSO_optimizer(**pso_kwargs)

t_end = 0.5
duration_bound = (0.01, t_end)
bounds0 = (
    (0, I_max_abs),
    duration_bound
)
pso_kwargs_pb_0 = {
    "dimensions" : 2,
    "bounds" : bounds0,
    "comment":"pulse"}


n_proc_list = [1, 2, 3, 4, None]
best_res_list = []
duration_list = []
# Problem definition
fig_costs, axs_costs = plt.subplots(2, 1)

for n_proc in n_proc_list:
    np.random.seed(444)
    my_prob = nrv.Problem(n_proc=n_proc)
    my_prob.costfunction = my_cost0
    my_prob.optimizer = pso_opt
    res0 = my_prob(**pso_kwargs_pb_0)
    best_res_list += [res0["x"]]
    duration_list += [res0["optimization_time"]]


    print("best input vector:", res0["x"], "\nbest cost:", res0["best_cost"])


    stim = cm_0(res0.x, static_context).extra_stim.stimuli[0]
    stim.plot(axs_costs[0], label="rectangle pulse")
    axs_costs[0].set_xlabel("best stimulus shape")
    axs_costs[0].set_xlabel("time (ms)")
    axs_costs[0].set_ylabel("amplitude (µA)")
    axs_costs[0].grid()

    res0.plot_cost_history(axs_costs[1])
    axs_costs[1].set_xlabel("optimization iteration")
    axs_costs[1].set_ylabel("cost")
    axs_costs[1].grid()
    fig_costs.tight_layout()

    simres = res0.compute_best_pos(my_cost0)
    simres.rasterize("V_mem")
    del my_prob




NRV INFO: Mesh properties:
NRV INFO: Number of processes : 3
NRV INFO: Number of entities : 36
NRV INFO: Number of nodes : 11390
NRV INFO: Number of elements : 80986
NRV INFO: Static/Quasi-Static electrical current problem
NRV INFO: FEN4NRV: setup the bilinear form
NRV INFO: FEN4NRV: setup the linear form
NRV INFO: Static/Quasi-Static electrical current problem
NRV INFO: FEN4NRV: solving electrical potential
NRV INFO: FEN4NRV: solved in 4.018976449966431 s
PSO optimizer - 1 proc: 100%|██████████|50/50, best_cost=0.0295
best input vector: [3.989542355705061, 0.1852817779181298]
best cost: 0.029493916445033925
PSO optimizer - 2 procs: 100%|██████████|50/50, best_cost=0.0295
best input vector: [3.989542355705061, 0.1852817779181298]
best cost: 0.029493916445033925
PSO optimizer - 3 procs: 100%|██████████|50/50, best_cost=0.0295
best input vector: [3.989542355705061, 0.1852817779181298]
best cost: 0.029493916445033925
PSO optimizer - 4 procs: 100%|██████████|50/50, best_cost=0.0295
best input vector: [3.989542355705061, 0.1852817779181298]
best cost: 0.029493916445033925
PSO optimizer - 3 procs: 100%|██████████|50/50, best_cost=0.0295
best input vector: [3.989542355705061, 0.1852817779181298]
best cost: 0.029493916445033925
../../_images/examples_optim_o06_nerve_optimization_1_11.png
../../_images/examples_optim_o06_nerve_optimization_1_12.png
[2]:
plt.figure()
n_proc_list_int = [1, 2, 3, 4, 5]
n_proc_list_labs = [str(i) for i in n_proc_list_int]
n_proc_list_labs[-1] = f"default = {nrv.parameters.optim_Ncores}"

plt.plot(n_proc_list_int, duration_list, "-+k")
plt.xticks(n_proc_list_int, labels=n_proc_list_labs)
plt.xlabel("Number of process")
plt.ylabel("PSO duration (s)")
plt.grid()
../../_images/examples_optim_o06_nerve_optimization_2_0.png