Xopt CNSGA algorithm¶
In [1]:
Copied!
from xopt.generators.ga.cnsga import CNSGAGenerator
from xopt.resources.test_functions.tnk import evaluate_TNK, tnk_vocs
from xopt.utils import read_xopt_csv
from xopt import Xopt, Evaluator
import pandas as pd
from glob import glob
import matplotlib.pyplot as plt
from xopt.generators.ga.cnsga import CNSGAGenerator
from xopt.resources.test_functions.tnk import evaluate_TNK, tnk_vocs
from xopt.utils import read_xopt_csv
from xopt import Xopt, Evaluator
import pandas as pd
from glob import glob
import matplotlib.pyplot as plt
In [2]:
Copied!
# Useful for debugging
# %load_ext autoreload
# %autoreload 2
# Useful for debugging
# %load_ext autoreload
# %autoreload 2
In [3]:
Copied!
ev = Evaluator(function=evaluate_TNK)
ev.function_kwargs = {
"raise_probability": 0.1
} # optional random crashing, to mimic real-world use.
ev = Evaluator(function=evaluate_TNK)
ev.function_kwargs = {
"raise_probability": 0.1
} # optional random crashing, to mimic real-world use.
In [4]:
Copied!
X = Xopt(
generator=CNSGAGenerator(vocs=tnk_vocs),
evaluator=ev,
vocs=tnk_vocs,
)
X.strict = False
X = Xopt(
generator=CNSGAGenerator(vocs=tnk_vocs),
evaluator=ev,
vocs=tnk_vocs,
)
X.strict = False
Run 100 generations
In [5]:
Copied!
%%time
for _ in range(64 * 20):
X.step()
%%time
for _ in range(64 * 20):
X.step()
CPU times: user 3.13 s, sys: 16.4 ms, total: 3.14 s Wall time: 3.27 s
Plot¶
In [6]:
Copied!
def plot_population(X):
fig, ax = plt.subplots(figsize=(8, 8))
fdata = tnk_vocs.feasibility_data(X.data)
k1 = "x1"
k2 = "x2"
X.data.plot.scatter(k1, k2, marker=".", alpha=0.1, color="black", ax=ax)
X.data[fdata["feasible"]].plot.scatter(
k1, k2, marker="x", alpha=0.3, color="orange", ax=ax
)
X.generator.population.plot.scatter(k1, k2, marker="o", color="red", alpha=1, ax=ax)
ax.set_xlabel(k1)
ax.set_ylabel(k2)
ax.set_xlim(0, 1.5)
ax.set_ylim(0, 1.5)
ax.set_title("TNK with Xopt's CNSGA")
def plot_population(X):
fig, ax = plt.subplots(figsize=(8, 8))
fdata = tnk_vocs.feasibility_data(X.data)
k1 = "x1"
k2 = "x2"
X.data.plot.scatter(k1, k2, marker=".", alpha=0.1, color="black", ax=ax)
X.data[fdata["feasible"]].plot.scatter(
k1, k2, marker="x", alpha=0.3, color="orange", ax=ax
)
X.generator.population.plot.scatter(k1, k2, marker="o", color="red", alpha=1, ax=ax)
ax.set_xlabel(k1)
ax.set_ylabel(k2)
ax.set_xlim(0, 1.5)
ax.set_ylim(0, 1.5)
ax.set_title("TNK with Xopt's CNSGA")
In [7]:
Copied!
plot_population(X)
plot_population(X)
Write the current population
In [8]:
Copied!
X.generator.write_population("test.csv")
X.generator.write_population("test.csv")
YAML method¶
In [9]:
Copied!
YAML = """
max_evaluations: 6400
strict: False
generator:
name: cnsga
population_size: 64
population_file: test.csv
output_path: .
evaluator:
function: xopt.resources.test_functions.tnk.evaluate_TNK
function_kwargs:
raise_probability: 0.1
vocs:
variables:
x1: [0, 3.14159]
x2: [0, 3.14159]
objectives: {y1: MINIMIZE, y2: MINIMIZE}
constraints:
c1: [GREATER_THAN, 0]
c2: [LESS_THAN, 0.5]
constants: {a: dummy_constant}
"""
X = Xopt(YAML)
X
YAML = """
max_evaluations: 6400
strict: False
generator:
name: cnsga
population_size: 64
population_file: test.csv
output_path: .
evaluator:
function: xopt.resources.test_functions.tnk.evaluate_TNK
function_kwargs:
raise_probability: 0.1
vocs:
variables:
x1: [0, 3.14159]
x2: [0, 3.14159]
objectives: {y1: MINIMIZE, y2: MINIMIZE}
constraints:
c1: [GREATER_THAN, 0]
c2: [LESS_THAN, 0.5]
constants: {a: dummy_constant}
"""
X = Xopt(YAML)
X
Out[9]:
Xopt ________________________________ Version: 2.4.6.dev5+ga295b108.d20250107 Data size: 0 Config as YAML: dump_file: null evaluator: function: xopt.resources.test_functions.tnk.evaluate_TNK function_kwargs: raise_probability: 0.1 random_sleep: 0 sleep: 0 max_workers: 1 vectorized: false generator: crossover_probability: 0.9 mutation_probability: 1.0 name: cnsga output_path: . population: null population_file: test.csv population_size: 64 supports_multi_objective: true max_evaluations: 6400 serialize_inline: false serialize_torch: false strict: false vocs: constants: a: dummy_constant constraints: c1: - GREATER_THAN - 0.0 c2: - LESS_THAN - 0.5 objectives: y1: MINIMIZE y2: MINIMIZE observables: [] variables: x1: - 0.0 - 3.14159 x2: - 0.0 - 3.14159
This will have loaded children from the population file. These will need to be re-evaluated.
In [10]:
Copied!
len(X.generator._children)
len(X.generator._children)
Out[10]:
64
In [11]:
Copied!
%%time
X.run()
%%time
X.run()
CPU times: user 15.9 s, sys: 69.2 ms, total: 16 s Wall time: 16.7 s
In [12]:
Copied!
plot_population(X)
plot_population(X)
In [13]:
Copied!
len(X.data)
len(X.data)
Out[13]:
6400
Setting output_path
will write .csv files for each population, as well as the offspring considered in each generation
In [14]:
Copied!
pop_files = sorted(glob("cnsga_population*"))
pop_files[:10]
pop_files = sorted(glob("cnsga_population*"))
pop_files[:10]
Out[14]:
['cnsga_population_2025-01-07T16_06_03.172644+00_00.csv', 'cnsga_population_2025-01-07T16_06_03.332301+00_00.csv', 'cnsga_population_2025-01-07T16_06_03.489521+00_00.csv', 'cnsga_population_2025-01-07T16_06_03.649259+00_00.csv', 'cnsga_population_2025-01-07T16_06_03.803697+00_00.csv', 'cnsga_population_2025-01-07T16_06_03.964673+00_00.csv', 'cnsga_population_2025-01-07T16_06_04.125827+00_00.csv', 'cnsga_population_2025-01-07T16_06_04.285418+00_00.csv', 'cnsga_population_2025-01-07T16_06_04.447338+00_00.csv', 'cnsga_population_2025-01-07T16_06_04.605961+00_00.csv']
In [15]:
Copied!
offspring_files = sorted(glob("cnsga_offspring*"))
offspring_files[0:10]
offspring_files = sorted(glob("cnsga_offspring*"))
offspring_files[0:10]
Out[15]:
['cnsga_offspring_2025-01-07T16_06_03.171443+00_00.csv', 'cnsga_offspring_2025-01-07T16_06_03.331230+00_00.csv', 'cnsga_offspring_2025-01-07T16_06_03.488409+00_00.csv', 'cnsga_offspring_2025-01-07T16_06_03.648161+00_00.csv', 'cnsga_offspring_2025-01-07T16_06_03.802631+00_00.csv', 'cnsga_offspring_2025-01-07T16_06_03.963579+00_00.csv', 'cnsga_offspring_2025-01-07T16_06_04.124679+00_00.csv', 'cnsga_offspring_2025-01-07T16_06_04.284335+00_00.csv', 'cnsga_offspring_2025-01-07T16_06_04.446237+00_00.csv', 'cnsga_offspring_2025-01-07T16_06_04.604856+00_00.csv']
In [16]:
Copied!
pop_df = read_xopt_csv(pop_files[-1])
pop_df.plot.scatter("x1", "x2", marker="o", color="red", alpha=1)
pop_df = read_xopt_csv(pop_files[-1])
pop_df.plot.scatter("x1", "x2", marker="o", color="red", alpha=1)
Out[16]:
<Axes: xlabel='x1', ylabel='x2'>
Similarly, offsrping files can be loaded. This will load the last few:
In [17]:
Copied!
offspring_df = read_xopt_csv(*offspring_files[-10:])
offspring_df.plot.scatter("x1", "x2", marker=".", color="black", alpha=0.1)
offspring_df = read_xopt_csv(*offspring_files[-10:])
offspring_df.plot.scatter("x1", "x2", marker=".", color="black", alpha=0.1)
Out[17]:
<Axes: xlabel='x1', ylabel='x2'>
Occationally there are duplicates in offspring
In [18]:
Copied!
all_offspring = read_xopt_csv(*offspring_files)
len(all_offspring), len(all_offspring.drop_duplicates())
all_offspring = read_xopt_csv(*offspring_files)
len(all_offspring), len(all_offspring.drop_duplicates())
Out[18]:
(6400, 6398)
In [19]:
Copied!
# Cleanup
!rm cnsga_population*
!rm cnsga_offspring*
!rm test.csv
# Cleanup
!rm cnsga_population*
!rm cnsga_offspring*
!rm test.csv
Examine generator¶
In [20]:
Copied!
df = pd.DataFrame(X.generator.generate(1000))
fig, ax = plt.subplots()
df.plot.scatter(
"x1", "x2", marker=".", color="green", alpha=0.5, ax=ax, label="candidates"
)
pop_df.plot.scatter(
"x1", "x2", marker="o", color="red", alpha=1, ax=ax, label="population"
)
plt.legend()
df = pd.DataFrame(X.generator.generate(1000))
fig, ax = plt.subplots()
df.plot.scatter(
"x1", "x2", marker=".", color="green", alpha=0.5, ax=ax, label="candidates"
)
pop_df.plot.scatter(
"x1", "x2", marker="o", color="red", alpha=1, ax=ax, label="population"
)
plt.legend()
Out[20]:
<matplotlib.legend.Legend at 0x7f2b6e542e90>
Vectorized evaluation¶
Some functions also allow vectorized inputs. This can often be very fast.
However, vectorized evaluation has some restrictions. For example, the output dict cannot append additional arrays with odd lengths.
In [21]:
Copied!
# Notice that this returns `some_array`
evaluate_TNK({"x1": 1, "x2": 1})
# Notice that this returns `some_array`
evaluate_TNK({"x1": 1, "x2": 1})
Out[21]:
{'y1': 1, 'y2': 1, 'c1': np.float64(0.9), 'c2': 0.5}
In [22]:
Copied!
# Here we make a version that does not have this
def evaluate_TNK2(*args, **kwargs):
outputs = evaluate_TNK(*args, **kwargs)
outputs.pop("some_array")
return outputs
# Here we make a version that does not have this
def evaluate_TNK2(*args, **kwargs):
outputs = evaluate_TNK(*args, **kwargs)
outputs.pop("some_array")
return outputs
In [23]:
Copied!
YAML = """
max_evaluations: 6400
strict: False
generator:
name: cnsga
population_size: 64
evaluator:
function: __main__.evaluate_TNK2
function_kwargs:
raise_probability: 0.1
vectorized: True
max_workers: 100
vocs:
variables:
x1: [0, 3.14159]
x2: [0, 3.14159]
objectives: {y1: MINIMIZE, y2: MINIMIZE}
constraints:
c1: [GREATER_THAN, 0]
c2: [LESS_THAN, 0.5]
constants: {a: dummy_constant}
"""
X2 = Xopt.from_yaml(YAML)
X2.evaluator.function = evaluate_TNK2
X2.run()
len(X2.data)
YAML = """
max_evaluations: 6400
strict: False
generator:
name: cnsga
population_size: 64
evaluator:
function: __main__.evaluate_TNK2
function_kwargs:
raise_probability: 0.1
vectorized: True
max_workers: 100
vocs:
variables:
x1: [0, 3.14159]
x2: [0, 3.14159]
objectives: {y1: MINIMIZE, y2: MINIMIZE}
constraints:
c1: [GREATER_THAN, 0]
c2: [LESS_THAN, 0.5]
constants: {a: dummy_constant}
"""
X2 = Xopt.from_yaml(YAML)
X2.evaluator.function = evaluate_TNK2
X2.run()
len(X2.data)
Out[23]:
6400
In [24]:
Copied!
plot_population(X)
plot_population(X)