Skip to content

Commit

Permalink
merge validation to main
Browse files Browse the repository at this point in the history
  • Loading branch information
b4pm-devops committed Aug 27, 2024
2 parents 656e184 + dc9a4f9 commit 613d648
Show file tree
Hide file tree
Showing 9 changed files with 449 additions and 116 deletions.
2 changes: 2 additions & 0 deletions scripts/run_all_tests.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@echo off
call %CD%\..\..\..\scripts\generic_test_script.bat sostrades_optimization_plugins
36 changes: 29 additions & 7 deletions sostrades_optimization_plugins/models/func_manager/func_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
from sostrades_core.tools.base_functions.exp_min import compute_func_with_exp_min

from sostrades_optimization_plugins.tools.cst_manager.func_manager_common import (
keep_negative_only,
keep_negative_only_square,
keep_positive_only,
keep_positive_only_square,
smooth_maximum,
)

Expand All @@ -35,10 +39,17 @@ class FunctionManager:
WEIGHT = 'weight' # Can be used for normalisation
AGGR = 'aggr'
AGGR_TYPE_SMAX = 'smax'
INEQ_NEGATIVE_WHEN_SATIFIED = 'negative_when_satisfied'
INEQ_POSITIVE_WHEN_SATIFIED = 'positive_when_satisfied'
INEQ_NEGATIVE_WHEN_SATIFIED_AND_SQUARE_IT = 'negative_when_satisfied_square_it'
INEQ_POSITIVE_WHEN_SATIFIED_AND_SQUARE_IT = 'positive_when_satisfied_square_it'
AGGR_TYPE_SUM = 'sum'
AGGR_TYPE_MEAN = 'mean'
AGGR_TYPE_DELTA = 'delta'
AGGR_TYPE_LIN_TO_QUAD = 'lin_to_quad'
POS_AGGR_TYPE = [AGGR_TYPE_SMAX, AGGR_TYPE_SUM, AGGR_TYPE_DELTA, AGGR_TYPE_LIN_TO_QUAD]
POS_AGGR_TYPE = [AGGR_TYPE_SMAX, AGGR_TYPE_SUM, AGGR_TYPE_DELTA, AGGR_TYPE_LIN_TO_QUAD,
AGGR_TYPE_MEAN, INEQ_NEGATIVE_WHEN_SATIFIED, INEQ_POSITIVE_WHEN_SATIFIED,
INEQ_NEGATIVE_WHEN_SATIFIED_AND_SQUARE_IT, INEQ_POSITIVE_WHEN_SATIFIED_AND_SQUARE_IT]

def __init__(self):
"""
Expand Down Expand Up @@ -125,20 +136,31 @@ def scalarize_all_functions(self, eps=1e-3, alpha=3):
if self.functions[tag][self.FTYPE] == self.OBJECTIVE:
#-- smooth maximum of values return the value if it was a float
#-- return smooth maximum if objective was an array
if aggr_type == 'smax':
if aggr_type == self.AGGR_TYPE_SMAX:
res = smooth_maximum(values, alpha)
elif aggr_type == 'sum':
elif aggr_type == self.AGGR_TYPE_SUM:
res = values.sum()
elif aggr_type == self.AGGR_TYPE_MEAN:
res = values.mean()
else:
raise Exception(f"Unhandled aggr_type {aggr_type}")
elif self.functions[tag][self.FTYPE] == self.INEQ_CONSTRAINT:
#-- scale between (0., +inf) and take smooth maximum
cst = self.cst_func_ineq(values, eps, tag)
res = smooth_maximum(cst, alpha)
if aggr_type == self.INEQ_NEGATIVE_WHEN_SATIFIED:
res = keep_positive_only(values)
elif aggr_type == self.INEQ_POSITIVE_WHEN_SATIFIED:
res = keep_negative_only(values)
elif aggr_type == self.INEQ_NEGATIVE_WHEN_SATIFIED_AND_SQUARE_IT:
res = keep_positive_only_square(values)
elif aggr_type == self.INEQ_POSITIVE_WHEN_SATIFIED_AND_SQUARE_IT:
res = keep_negative_only_square(values)
else:
cst = self.cst_func_ineq(values, eps, tag)
res = smooth_maximum(cst, alpha)
elif self.functions[tag][self.FTYPE] == self.EQ_CONSTRAINT:
if aggr_type == 'delta':
if aggr_type == self.AGGR_TYPE_DELTA:
cst = self.cst_func_eq_delta(values, eps, tag)
elif aggr_type == 'lin_to_quad':
elif aggr_type == self.AGGR_TYPE_LIN_TO_QUAD:
cst = self.cst_func_eq_lintoquad(values, eps, tag)
else:
cst = self.cst_func_eq(values)
Expand Down
192 changes: 116 additions & 76 deletions sostrades_optimization_plugins/models/func_manager/func_manager_disc.py

Large diffs are not rendered by default.

246 changes: 236 additions & 10 deletions sostrades_optimization_plugins/tests/l0_test_44_func_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@ def test_07_jacobian_func_manager_disc(self):

cst3 = base_df.copy()
cst3['cst3_values'] = np.array([-10., 0.2, -5.])
cst0 = base_df.copy()
cst0['cst0_values'] = np.array([-10., 0.2, -5.])
eqcst1 = base_df.copy()
eqcst1['eqcst1_values'] = np.array([-10., 10., -5.])
eqcst2 = base_df.copy()
Expand All @@ -281,12 +283,12 @@ def test_07_jacobian_func_manager_disc(self):
# -- ~GUI inputs: selection of functions

func_df = pd.DataFrame(columns=['variable', 'ftype', 'weight'])
func_df['variable'] = ['cst1', 'cst2', 'cst3',
func_df['variable'] = ['cst0','cst1', 'cst2', 'cst3',
'eqcst1', 'eqcst2', 'obj1', 'obj2']
func_df['ftype'] = [INEQ_CONSTRAINT, INEQ_CONSTRAINT,
func_df['ftype'] = [INEQ_CONSTRAINT, INEQ_CONSTRAINT, INEQ_CONSTRAINT,
INEQ_CONSTRAINT, EQ_CONSTRAINT, EQ_CONSTRAINT, OBJECTIVE, OBJECTIVE]
func_df['weight'] = [1., 1., 1., 1, 1, 0.8, 0.2]
func_df['aggr'] = "sum"
func_df['weight'] = [1, 1., 1., 1., 1, 1, 0.8, 0.2]
func_df['aggr'] = [FunctionManager.INEQ_NEGATIVE_WHEN_SATIFIED] + [FunctionManager.AGGR_TYPE_SUM] * 7
func_df['parent'] = 'obj'
func_df['namespace'] = ''

Expand All @@ -298,6 +300,7 @@ def test_07_jacobian_func_manager_disc(self):
values_dict[prefix + 'cst1'] = cst1
values_dict[prefix + 'cst2'] = cst2
values_dict[prefix + 'cst3'] = cst3
values_dict[prefix + 'cst0'] = cst0
values_dict[prefix + 'eqcst1'] = eqcst1
values_dict[prefix + 'eqcst2'] = eqcst2
values_dict[prefix + 'obj1'] = obj1
Expand Down Expand Up @@ -331,7 +334,9 @@ def test_07_jacobian_func_manager_disc(self):

assert disc_techno.check_jacobian(
input_data=disc_techno.local_data,
threshold=1e-5, inputs=['FuncManagerTest.FunctionManager.cst1', 'FuncManagerTest.FunctionManager.cst2',
threshold=1e-5, inputs=[
'FuncManagerTest.FunctionManager.cst0',
'FuncManagerTest.FunctionManager.cst1', 'FuncManagerTest.FunctionManager.cst2',
'FuncManagerTest.FunctionManager.cst3', 'FuncManagerTest.FunctionManager.obj1',
'FuncManagerTest.FunctionManager.obj2'],
outputs=['FuncManagerTest.FunctionManager.objective_lagrangian'], derr_approx='complex_step')
Expand Down Expand Up @@ -609,7 +614,7 @@ def test_11_jacobian_eq_delta_and_lin_to_quad(self):
obj2['obj2_values'] = 1.
ineq_cst = base_df.copy()
ineq_cst['ineq_cst_values'] = np.array([10., -2000., -30.])
ineq_cst_array = np.array([10., 2000., -30.])
ineq_cst0 = np.array([10., 2000., -30.])
eqcst_delta = base_df.copy()
eqcst_delta['eqcst_delta_values'] = np.array([400., 1., -10.])
eqcst_delta2 = base_df.copy()
Expand All @@ -622,7 +627,7 @@ def test_11_jacobian_eq_delta_and_lin_to_quad(self):
# -- ~GUI inputs: selection of functions

func_df = pd.DataFrame(columns=['variable', 'ftype', 'weight', 'aggr'])
func_df['variable'] = ['ineq_cst', 'ineq_cst_array', 'eqcst_delta', 'eqcst_delta2',
func_df['variable'] = ['ineq_cst', 'ineq_cst0', 'eqcst_delta', 'eqcst_delta2',
'eqcst_delta_array', 'eqcst_lintoquad', 'eqcst_lintoquad_array',
'obj1', 'obj2']
func_df['ftype'] = [INEQ_CONSTRAINT, INEQ_CONSTRAINT,
Expand All @@ -637,7 +642,7 @@ def test_11_jacobian_eq_delta_and_lin_to_quad(self):

# -- data to simulate disciplinary chain outputs
values_dict[prefix + 'ineq_cst'] = ineq_cst
values_dict[prefix + 'ineq_cst_array'] = ineq_cst_array
values_dict[prefix + 'ineq_cst0'] = ineq_cst0
values_dict[prefix + 'eqcst_delta'] = eqcst_delta
values_dict[prefix + 'eqcst_delta2'] = eqcst_delta2
values_dict[prefix + 'eqcst_delta_array'] = eqcst_delta_array
Expand All @@ -650,7 +655,7 @@ def test_11_jacobian_eq_delta_and_lin_to_quad(self):

ee.load_study_from_input_dict(values_dict)

ee.dm.set_data(prefix + 'ineq_cst_array', 'type', 'array')
ee.dm.set_data(prefix + 'ineq_cst0', 'type', 'array')
ee.dm.set_data(prefix + 'eqcst_delta_array', 'type', 'array')
ee.dm.set_data(prefix + 'eqcst_lintoquad_array', 'type', 'array')

Expand Down Expand Up @@ -678,7 +683,7 @@ def test_11_jacobian_eq_delta_and_lin_to_quad(self):
assert disc_techno.check_jacobian(
input_data=disc_techno.local_data,
threshold=1e-8, inputs=['FuncManagerTest.FunctionManager.ineq_cst',
'FuncManagerTest.FunctionManager.ineq_cst_array',
'FuncManagerTest.FunctionManager.ineq_cst0',
'FuncManagerTest.FunctionManager.eqcst_delta',
'FuncManagerTest.FunctionManager.eqcst_delta2',
'FuncManagerTest.FunctionManager.eqcst_delta_array',
Expand Down Expand Up @@ -726,3 +731,224 @@ def test_12_test_number_iteration_output_optim_df(self):
func_disc = self.ee.dm.get_disciplines_with_name(f'{self.name}.{optim_name}.SellarCoupling.FunctionManager')[0]
filter = func_disc.get_chart_filter_list()
graph_list = func_disc.get_post_processing_list(filter)

def test_13_jacobian_func_manager_disc_ineq_constraint_negative_when_satisfied(self):
INEQ_CONSTRAINT = self.func_manager.INEQ_CONSTRAINT

# -- init the case
func_mng_name = 'FunctionManager'
prefix = self.name + '.' + func_mng_name + '.'

ee = ExecutionEngine(self.name)
ns_dict = {'ns_functions': self.name + '.' + func_mng_name,
'ns_optim': self.name + '.' + func_mng_name}
ee.ns_manager.add_ns_def(ns_dict)

mod_list = 'sostrades_optimization_plugins.models.func_manager.func_manager_disc.FunctionManagerDisc'
fm_builder = ee.factory.get_builder_from_module(
'FunctionManager', mod_list)
ee.factory.set_builders_to_coupling_builder(fm_builder)
ee.configure()

# -- i/o setup
base_df = pd.DataFrame({'years': arange(10, 13)})
cst0 = base_df.copy()
cst0['cst0_values'] = np.array([-10., 1, -5.])

# -- ~GUI inputs: selection of functions

func_df = pd.DataFrame(columns=['variable', 'ftype', 'weight'])
func_df['variable'] = ['cst0']
func_df['ftype'] = [INEQ_CONSTRAINT]
func_df['weight'] = [2.]
func_df['aggr'] = [FunctionManager.INEQ_NEGATIVE_WHEN_SATIFIED]
func_df['parent'] = 'obj'
func_df['namespace'] = ''


values_dict = {}
values_dict[prefix + FunctionManagerDisc.FUNC_DF] = func_df

# -- data to simulate disciplinary chain outputs
values_dict[prefix + 'cst0'] = cst0

ee.load_study_from_input_dict(values_dict)

ee.display_treeview_nodes(True)

# -- execution
ee.execute()
# -- retrieve outputs
disc = ee.dm.get_disciplines_with_name(
f'{self.name}.{func_mng_name}')[0]
disc_techno = ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline

assert disc_techno.check_jacobian(
input_data=disc_techno.local_data,
threshold=1e-5, inputs=['FuncManagerTest.FunctionManager.cst0'],
outputs=['FuncManagerTest.FunctionManager.objective_lagrangian'], derr_approx='complex_step', step=1e-15)

def test_14_jacobian_func_manager_disc_ineq_constraint_positive_when_satisfied(self):
INEQ_CONSTRAINT = self.func_manager.INEQ_CONSTRAINT

# -- init the case
func_mng_name = 'FunctionManager'
prefix = self.name + '.' + func_mng_name + '.'

ee = ExecutionEngine(self.name)
ns_dict = {'ns_functions': self.name + '.' + func_mng_name,
'ns_optim': self.name + '.' + func_mng_name}
ee.ns_manager.add_ns_def(ns_dict)

mod_list = 'sostrades_optimization_plugins.models.func_manager.func_manager_disc.FunctionManagerDisc'
fm_builder = ee.factory.get_builder_from_module(
'FunctionManager', mod_list)
ee.factory.set_builders_to_coupling_builder(fm_builder)
ee.configure()

# -- i/o setup
base_df = pd.DataFrame({'years': arange(10, 13)})
cst0 = base_df.copy()
cst0['cst0_values'] = np.array([-10., 0.2, -5.])

# -- ~GUI inputs: selection of functions

func_df = pd.DataFrame(columns=['variable', 'ftype', 'weight'])
func_df['variable'] = ['cst0']
func_df['ftype'] = [INEQ_CONSTRAINT]
func_df['weight'] = [3.]
func_df['aggr'] = [FunctionManager.INEQ_POSITIVE_WHEN_SATIFIED]
func_df['parent'] = 'obj'
func_df['namespace'] = ''

values_dict = {}
values_dict[prefix + FunctionManagerDisc.FUNC_DF] = func_df

# -- data to simulate disciplinary chain outputs
values_dict[prefix + 'cst0'] = cst0

ee.load_study_from_input_dict(values_dict)

ee.display_treeview_nodes(True)

# -- execution
ee.execute()
# -- retrieve outputs
disc = ee.dm.get_disciplines_with_name(
f'{self.name}.{func_mng_name}')[0]
disc_techno = ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline

assert disc_techno.check_jacobian(
input_data=disc_techno.local_data,
threshold=1e-5, inputs=['FuncManagerTest.FunctionManager.cst0'],
outputs=['FuncManagerTest.FunctionManager.objective_lagrangian'], derr_approx='complex_step')

def test_16_jacobian_func_manager_disc_ineq_constraint_negative_when_satisfied_square(self):
INEQ_CONSTRAINT = self.func_manager.INEQ_CONSTRAINT

# -- init the case
func_mng_name = 'FunctionManager'
prefix = self.name + '.' + func_mng_name + '.'

ee = ExecutionEngine(self.name)
ns_dict = {'ns_functions': self.name + '.' + func_mng_name,
'ns_optim': self.name + '.' + func_mng_name}
ee.ns_manager.add_ns_def(ns_dict)

mod_list = 'sostrades_optimization_plugins.models.func_manager.func_manager_disc.FunctionManagerDisc'
fm_builder = ee.factory.get_builder_from_module(
'FunctionManager', mod_list)
ee.factory.set_builders_to_coupling_builder(fm_builder)
ee.configure()

# -- i/o setup
base_df = pd.DataFrame({'years': arange(10, 13)})
cst0 = base_df.copy()
cst0['cst0_values'] = np.array([-10., 0.2, -5.])

# -- ~GUI inputs: selection of functions

func_df = pd.DataFrame(columns=['variable', 'ftype', 'weight'])
func_df['variable'] = ['cst0']
func_df['ftype'] = [INEQ_CONSTRAINT]
func_df['weight'] = [2.5]
func_df['aggr'] = [FunctionManager.INEQ_NEGATIVE_WHEN_SATIFIED_AND_SQUARE_IT]
func_df['parent'] = 'obj'
func_df['namespace'] = ''

values_dict = {}
values_dict[prefix + FunctionManagerDisc.FUNC_DF] = func_df

# -- data to simulate disciplinary chain outputs
values_dict[prefix + 'cst0'] = cst0

ee.load_study_from_input_dict(values_dict)

ee.display_treeview_nodes(True)

# -- execution
ee.execute()
# -- retrieve outputs
disc = ee.dm.get_disciplines_with_name(
f'{self.name}.{func_mng_name}')[0]
disc_techno = ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline

assert disc_techno.check_jacobian(
input_data=disc_techno.local_data,
threshold=1e-5, inputs=['FuncManagerTest.FunctionManager.cst0'],
outputs=['FuncManagerTest.FunctionManager.objective_lagrangian'], derr_approx='complex_step')

def test_17_jacobian_func_manager_disc_ineq_constraint_positive_when_satisfied_square(self):
INEQ_CONSTRAINT = self.func_manager.INEQ_CONSTRAINT

# -- init the case
func_mng_name = 'FunctionManager'
prefix = self.name + '.' + func_mng_name + '.'

ee = ExecutionEngine(self.name)
ns_dict = {'ns_functions': self.name + '.' + func_mng_name,
'ns_optim': self.name + '.' + func_mng_name}
ee.ns_manager.add_ns_def(ns_dict)

mod_list = 'sostrades_optimization_plugins.models.func_manager.func_manager_disc.FunctionManagerDisc'
fm_builder = ee.factory.get_builder_from_module(
'FunctionManager', mod_list)
ee.factory.set_builders_to_coupling_builder(fm_builder)
ee.configure()

# -- i/o setup
base_df = pd.DataFrame({'years': arange(10, 13)})
cst0 = base_df.copy()
cst0['cst0_values'] = np.array([-10., 0.2, -5.])

# -- ~GUI inputs: selection of functions

func_df = pd.DataFrame(columns=['variable', 'ftype', 'weight'])
func_df['variable'] = ['cst0']
func_df['ftype'] = [INEQ_CONSTRAINT]
func_df['weight'] = [2.]
func_df['aggr'] = [FunctionManager.INEQ_POSITIVE_WHEN_SATIFIED_AND_SQUARE_IT]
func_df['parent'] = 'obj'
func_df['namespace'] = ''

values_dict = {}
values_dict[prefix + FunctionManagerDisc.FUNC_DF] = func_df

# -- data to simulate disciplinary chain outputs
values_dict[prefix + 'cst0'] = cst0

ee.load_study_from_input_dict(values_dict)

ee.display_treeview_nodes(True)

# -- execution
ee.execute()
# -- retrieve outputs
disc = ee.dm.get_disciplines_with_name(
f'{self.name}.{func_mng_name}')[0]
disc_techno = ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline

assert disc_techno.check_jacobian(
input_data=disc_techno.local_data,
threshold=1e-5, inputs=['FuncManagerTest.FunctionManager.cst0'],
outputs=['FuncManagerTest.FunctionManager.objective_lagrangian'], derr_approx='complex_step')
Loading

0 comments on commit 613d648

Please sign in to comment.