Skip to content

Commit

Permalink
Imprpoved obsidian.campaign code coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
kstone40 committed Aug 13, 2024
1 parent aeb43ae commit c44f725
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 16 deletions.
16 changes: 6 additions & 10 deletions obsidian/campaign/campaign.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def add_data(self, df: pd.DataFrame):

def clear_data(self):
"""Clears campaign data"""
self.data = None
self.data = pd.DataFrame()

@property
def optimizer(self) -> Optimizer:
Expand Down Expand Up @@ -189,12 +189,11 @@ def y(self) -> pd.Series | pd.DataFrame:
"""
Experimental response data
Raises:
ValueError: If no target(s) are specified.
"""
if not self.target:
raise ValueError('No target(s) specified')
return self.data[self.y_names]
if not self.data.empty:
return self.data[self.y_names]
else:
return None

@property
def response_max(self) -> float | pd.Series:
Expand Down Expand Up @@ -296,10 +295,7 @@ def load_state(cls,
new_campaign.data = pd.DataFrame(obj_dict['data'])
new_campaign.data.index = new_campaign.data.index.astype('int')

try:
new_campaign.iter = new_campaign.data['Iteration'].astype('int').max()
except KeyError:
new_campaign.iter = 0
new_campaign.iter = new_campaign.data['Iteration'].astype('int').max()

return new_campaign

Expand Down
11 changes: 6 additions & 5 deletions obsidian/campaign/explainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from obsidian.parameters import Param_Continuous, ParamSpace
from obsidian.optimizer import Optimizer
from obsidian.exceptions import UnfitError

import shap
from shap import KernelExplainer, Explanation
Expand Down Expand Up @@ -36,7 +37,7 @@ def __init__(self,
X_space: ParamSpace | None = None) -> None:

if not optimizer.is_fit:
raise ValueError('Surrogate model in optimizer is not fit to data. ')
raise UnfitError('Surrogate model in optimizer is not fit to data. ')

self.set_optimizer(optimizer)
self.X_space = optimizer.X_space if X_space is None else X_space
Expand Down Expand Up @@ -117,7 +118,7 @@ def pred_func(X):
def shap_summary(self) -> Figure:
"""SHAP Summary Plot (Beeswarm)"""
if not self.shap:
raise ValueError('shap explainer is not fit.')
raise UnfitError('SHAP explainer is not fit.')

fig = plt.figure()
shap.summary_plot(self.shap['values'], self.shap['X_sample'],
Expand All @@ -129,7 +130,7 @@ def shap_summary(self) -> Figure:
def shap_summary_bar(self) -> Figure:
"""SHAP Summary Plot (Bar Plot / Importance)"""
if not self.shap:
raise ValueError('shap explainer is not fit.')
raise UnfitError('SHAP explainer is not fit.')

fig = plt.figure()
shap.plots.bar(self.shap['explanation'],
Expand Down Expand Up @@ -159,7 +160,7 @@ def shap_pdp_ice(self,
"""
if not self.shap:
raise ValueError('shap explainer is not fit.')
raise UnfitError('SHAP explainer is not fit.')

fig, ax = partial_dependence(
ind=ind,
Expand Down Expand Up @@ -193,7 +194,7 @@ def shap_single_point(self,
"""
if not self.shap:
raise ValueError('shap explainer is not fit.')
raise UnfitError('SHAP explainer is not fit.')

if isinstance(X_new, pd.Series):
X_new = X_new.copy().to_frame().T
Expand Down
74 changes: 73 additions & 1 deletion obsidian/tests/test_campaign.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from obsidian.objectives import Identity_Objective, Scalar_WeightedNorm, Feature_Objective, \
Objective_Sequence, Utopian_Distance, Index_Objective, Bounded_Target
from obsidian.plotting import plot_interactions, plot_ofat_ranges
from obsidian.exceptions import IncompatibleObjectiveError, UnfitError


from obsidian.tests.utils import DEFAULT_MOO_PATH
Expand Down Expand Up @@ -35,12 +36,18 @@ def test_campaign(X_space, sim_fcn, target):
y0 = simulator.simulate(X0)
Z0 = pd.concat([X0, y0], axis=1)
campaign.m_exp

# Test some conditional usage
campaign.add_data(Z0)
campaign.fit()
campaign.clear_data()
campaign.y

Z0['Iteration'] = 5
campaign.add_data(Z0)
campaign.y
campaign.fit()
campaign.response_max

obj_dict = campaign.save_state()
campaign2 = Campaign.load_state(obj_dict)
Expand Down Expand Up @@ -70,6 +77,7 @@ def test_campaign_objectives(obj):
campaign.set_objective(obj)
if campaign.objective:
campaign.objective.__repr__()
campaign.o

obj_dict = campaign.save_state()
campaign2 = Campaign.load_state(obj_dict)
Expand All @@ -80,6 +88,7 @@ def test_campaign_objectives(obj):

def test_explain():
exp = Explainer(campaign.optimizer)
exp.__repr__
exp.shap_explain(n=50)

exp.shap_summary()
Expand All @@ -103,10 +112,73 @@ def test_explain():

@pytest.mark.parametrize('X_ref', X_ref_test)
def test_analysis(X_ref):
ofat_ranges, _ = calc_ofat_ranges(campaign.optimizer, threshold=0.5, X_ref=X_ref, calc_interacts=False)
ofat_ranges, cor = calc_ofat_ranges(campaign.optimizer, threshold=0.5, X_ref=X_ref)

plot_interactions(campaign.optimizer, cor)
plot_ofat_ranges(campaign.optimizer, ofat_ranges)

ofat_ranges, cor = calc_ofat_ranges(campaign.optimizer, threshold=9999, X_ref=X_ref)
plot_interactions(campaign.optimizer, cor)
plot_ofat_ranges(campaign.optimizer, ofat_ranges)


@pytest.mark.fast
def test_campaign_validation():

random_data = pd.DataFrame(data={'A': [1, 2, 3], 'B': [4, 5, 6]})
with pytest.raises(KeyError):
campaign.add_data(random_data)

with pytest.raises(KeyError):
campaign.add_data(campaign.X)

with pytest.raises(IncompatibleObjectiveError):
campaign.set_objective(Identity_Objective(mo=False))

with pytest.raises(ValueError):
campaign2 = Campaign(X_space, target)
campaign2.fit()


@pytest.mark.fast
def test_explainer_validation():

campaign2 = Campaign(X_space, target)
with pytest.raises(UnfitError):
exp = Explainer(campaign2.optimizer)

exp = Explainer(campaign.optimizer)
with pytest.raises(UnfitError):
exp.shap_summary()

with pytest.raises(UnfitError):
exp.shap_summary_bar()

with pytest.raises(UnfitError):
exp.shap_single_point(X_new=campaign.X_space.mean())

random_data = pd.DataFrame(data={'A': [1], 'B': [4]})
long_data = pd.DataFrame(data={'Parameter 1': [1, 2], 'Parameter 2': [1, 2]})

with pytest.raises(ValueError):
exp.shap_explain(n=50, X_ref=random_data)

with pytest.raises(ValueError):
exp.shap_explain(n=50, X_ref=long_data)

exp.shap_explain(n=50)

with pytest.raises(ValueError):
exp.shap_single_point(X_new=random_data)

with pytest.raises(ValueError):
exp.shap_single_point(X_new=campaign.X_space.mean(), X_ref=random_data)

with pytest.raises(ValueError):
exp.sensitivity(X_ref=random_data)

with pytest.raises(ValueError):
exp.sensitivity(X_ref=long_data)


if __name__ == '__main__':
Expand Down

0 comments on commit c44f725

Please sign in to comment.