Skip to content

Commit

Permalink
Rename decision_methods to decision_rules
Browse files Browse the repository at this point in the history
  • Loading branch information
richardlaugesen committed Sep 30, 2024
1 parent 55e8669 commit 35feeac
Show file tree
Hide file tree
Showing 17 changed files with 58 additions and 59 deletions.
4 changes: 2 additions & 2 deletions examples/basic_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"from ruv.utility_functions import cara\n",
"from ruv.economic_models import cost_loss, cost_loss_analytical_spend\n",
"from ruv.damage_functions import logistic\n",
"from ruv.decision_methods import optimise_over_forecast_distribution"
"from ruv.decision_rules import optimise_over_forecast_distribution"
],
"outputs": [],
"execution_count": 1
Expand Down Expand Up @@ -112,7 +112,7 @@
" 'utility_function': [cara, {'A': 0.3}],\n",
" 'economic_model': [cost_loss, cost_loss_analytical_spend, np.arange(0.05, 1, 0.05)],\n",
" 'decision_thresholds': np.array([0, 5, 12]),\n",
" 'decision_making_method': [optimise_over_forecast_distribution, None],\n",
" 'decision_rule': [optimise_over_forecast_distribution, None],\n",
"}"
],
"id": "999d5fbec7a7ffc3",
Expand Down
4 changes: 2 additions & 2 deletions examples/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from ruv.economic_models import *
from ruv.utility_functions import *
from ruv.helpers import *
from ruv.decision_methods import *
from ruv.decision_rules import *

def load_data(awrc, start_lt, end_lt, area, scenario='muthre', input_path='../../muthre_results/muthre_csv'):
print('Loading data')
Expand Down Expand Up @@ -152,7 +152,7 @@ def mm_to_m3s(results, area):
'utility_function': [cara, {'A': adjusted_risk_aversion}],
'economic_model': [cost_loss, cost_loss_analytical_spend, alphas],
'decision_thresholds': np.arange(0, 20, 2),
'decision_making_method': [optimise_over_forecast_distribution, None],
'decision_rule': [optimise_over_forecast_distribution, None],
}

results = {}
Expand Down
8 changes: 4 additions & 4 deletions examples/example_1.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@
"metadata": {},
"outputs": [],
"source": [
"decision_definition['decision_method'] = 'optimise_over_forecast_distribution'\n",
"decision_definition['decision_rule'] = 'optimise_over_forecast_distribution'\n",
"results_optim = relative_utility_value(obs, fcst_ens, ref, decision_definition, parallel_nodes)"
]
},
Expand All @@ -221,7 +221,7 @@
"metadata": {},
"outputs": [],
"source": [
"decision_definition['decision_method'] = 'critical_probability_threshold_fixed'\n",
"decision_definition['decision_rule'] = 'critical_probability_threshold_fixed'\n",
"\n",
"decision_definition['critical_probability_threshold'] = 0.1\n",
"results_fixed_10th = relative_utility_value(obs, fcst_ens, ref, decision_definition, parallel_nodes)\n",
Expand All @@ -239,7 +239,7 @@
"metadata": {},
"outputs": [],
"source": [
"decision_definition['decision_method'] = 'critical_probability_threshold_max_value'\n",
"decision_definition['decision_rule'] = 'critical_probability_threshold_max_value'\n",
"results_max = relative_utility_value(obs, fcst_ens, ref, decision_definition, parallel_nodes)"
]
},
Expand All @@ -249,7 +249,7 @@
"metadata": {},
"outputs": [],
"source": [
"decision_definition['decision_method'] = 'critical_probability_threshold_equals_par'\n",
"decision_definition['decision_rule'] = 'critical_probability_threshold_equals_par'\n",
"results_alpha = relative_utility_value(obs, fcst_ens, ref, decision_definition, parallel_nodes)"
]
},
Expand Down
2 changes: 1 addition & 1 deletion examples/example_2.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
" 'economic_model_params': np.array([0.6]),\n",
" 'damage_function': [logistic_zero, {'A': max_loss, 'k': 0.5, 'threshold': np.nanquantile(obs, 0.9)}],\n",
" 'economic_model': [cost_loss, cost_loss_analytical_spend],\n",
" 'decision_method': 'optimise_over_forecast_distribution'\n",
" 'decision_rule': 'optimise_over_forecast_distribution'\n",
"}\n",
"\n",
"ref = clim_ens # use 14-day moving average climatology as reference forecast (pre-caulcated in example dataset)"
Expand Down
2 changes: 1 addition & 1 deletion examples/example_3.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@
" 'utility_function': [cara, {'A': 0}], # minor risk aversion\n",
" 'economic_model': [cost_loss, cost_loss_analytical_spend],\n",
" 'decision_thresholds': None,\n",
" 'decision_method': 'critical_probability_threshold_equals_par',\n",
" 'decision_rule': 'critical_probability_threshold_equals_par',\n",
" 'damage_function': [logistic, {'k': 1, 'A': 1, 'threshold': np.nanquantile(obs, 0.99)}]\n",
"}\n",
"\n",
Expand Down
2 changes: 1 addition & 1 deletion examples/example_4.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@
" 'utility_function': [cara, {'A': 0.3}], # minor risk aversion\n",
" 'economic_model': [cost_loss, cost_loss_analytical_spend],\n",
" 'decision_thresholds': None,\n",
" 'decision_method': 'optimise_over_forecast_distribution',\n",
" 'decision_rule': 'optimise_over_forecast_distribution',\n",
" 'damage_function': [logistic, {'k': k, 'A': 1, 'threshold': np.nanquantile(obs, 0.99)}]\n",
"}\n",
"\n",
Expand Down
2 changes: 1 addition & 1 deletion examples/example_5.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
" 'utility_function': [cara, {'A': 0.3}], # minor risk aversion\n",
" 'economic_model': [cost_loss, cost_loss_analytical_spend],\n",
" 'decision_thresholds': None,\n",
" 'decision_method': 'optimise_over_forecast_distribution'\n",
" 'decision_rule': 'optimise_over_forecast_distribution'\n",
"}\n",
"\n",
"ref = clim_ens # use 14-day moving average climatology as the reference forecast"
Expand Down
2 changes: 1 addition & 1 deletion examples/example_6.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@
" 'economic_model_params': np.array([0.2]), # [0.1, 0.5, 0.9]),\n",
" 'economic_model': [cost_loss, cost_loss_analytical_spend],\n",
" 'decision_thresholds': None,\n",
" 'decision_method': 'optimise_over_forecast_distribution',\n",
" 'decision_rule': 'optimise_over_forecast_distribution',\n",
" 'damage_function': [logistic, {'k': 1, 'A':1, 'threshold': np.nanquantile(obs, 0.99)}]\n",
"}\n",
"\n",
Expand Down
2 changes: 1 addition & 1 deletion examples/example_7.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
" 'economic_model_params': alphas,\n",
" 'damage_function': [logistic_zero, {'A': 1, 'k': 0.5, 'threshold': np.nanquantile(obs, 0.8)}],\n",
" 'economic_model': [cost_loss, cost_loss_analytical_spend],\n",
" 'decision_method': 'critical_probability_threshold_equals_par',\n",
" 'decision_rule': 'critical_probability_threshold_equals_par',\n",
" 'utility_function': [cara, {'A': 0}]\n",
"}\n",
"\n",
Expand Down
8 changes: 4 additions & 4 deletions examples/example_8.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@
" 'economic_model_params': alphas,\n",
" 'damage_function': [logistic_zero, {'A': 1, 'k': 0.5, 'threshold': np.nanquantile(obs, 0.75)}],\n",
" 'economic_model': [cost_loss, cost_loss_analytical_spend],\n",
" 'decision_method': 'optimise_over_forecast_distribution',\n",
" #'decision_method': 'critical_probability_threshold_equals_par',\n",
" #'decision_method': 'critical_probability_threshold_fixed', # TODO: keeps returning almost flat value diagrams\n",
" #'decision_method': 'critical_probability_threshold_max_value',\n",
" 'decision_rule': 'optimise_over_forecast_distribution',\n",
" #'decision_rule': 'critical_probability_threshold_equals_par',\n",
" #'decision_rule': 'critical_probability_threshold_fixed', # TODO: keeps returning almost flat value diagrams\n",
" #'decision_rule': 'critical_probability_threshold_max_value',\n",
" 'critical_probability_threshold': 0.25,\n",
" 'utility_function': [cara, {'A': 0}],\n",
" 'event_freq_ref': True\n",
Expand Down
2 changes: 1 addition & 1 deletion ruv/data_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class DecisionContext:
decision_thresholds: np.ndarray
economic_model: Callable
analytical_spend: Callable
decision_making_method: Callable
decision_rule: Callable


@dataclass
Expand Down
17 changes: 8 additions & 9 deletions ruv/decision_methods.py → ruv/decision_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,36 +20,35 @@
from ruv.data_classes import MultiParOutput, DecisionContext
from ruv.helpers import probabilistic_to_deterministic_forecast


def optimise_over_forecast_distribution(params: dict) -> Callable:
# method has no params

def decision_method(obs: np.ndarray, fcsts: np.ndarray, refs: np.ndarray, context: DecisionContext, parallel_nodes: int) -> MultiParOutput:
def decision_rule(obs: np.ndarray, fcsts: np.ndarray, refs: np.ndarray, context: DecisionContext, parallel_nodes: int) -> MultiParOutput:
outputs = MultiParOutput()
for econ_par in context.economic_model_params:
outputs.insert(econ_par, multiple_timesteps(obs, fcsts, refs, econ_par, context, parallel_nodes))
return outputs

return decision_method
return decision_rule


def critical_probability_threshold_fixed(params: dict) -> Callable:
crit_prob_thres = params['critical_probability_threshold']

def decision_method(obs: np.ndarray, fcsts: np.ndarray, refs: np.ndarray, context: DecisionContext, parallel_nodes: int) -> MultiParOutput:
def decision_rule(obs: np.ndarray, fcsts: np.ndarray, refs: np.ndarray, context: DecisionContext, parallel_nodes: int) -> MultiParOutput:
fcsts = probabilistic_to_deterministic_forecast(fcsts, crit_prob_thres)
outputs = MultiParOutput()
for econ_par in context.economic_model_params:
outputs.insert(econ_par, multiple_timesteps(obs, fcsts, refs, econ_par, context, parallel_nodes))
return outputs

return decision_method
return decision_rule


def critical_probability_threshold_max_value(params: dict) -> Callable:
# method has no params

def decision_method(obs: np.ndarray, fcsts: np.ndarray, refs: np.ndarray, context: DecisionContext, parallel_nodes: int) -> MultiParOutput:
def decision_rule(obs: np.ndarray, fcsts: np.ndarray, refs: np.ndarray, context: DecisionContext, parallel_nodes: int) -> MultiParOutput:
outputs = MultiParOutput()
for econ_par in context.economic_model_params:
def minimise_this(crit_prob_thres):
Expand All @@ -61,17 +60,17 @@ def minimise_this(crit_prob_thres):
outputs.insert(econ_par, multiple_timesteps(obs, max_fcsts, refs, econ_par, context, parallel_nodes))
return outputs

return decision_method
return decision_rule


def critical_probability_threshold_equals_par(params: dict) -> Callable:
# method has no params

def decision_method(obs: np.ndarray, fcsts: np.ndarray, refs: np.ndarray, context: DecisionContext, parallel_nodes: int) -> MultiParOutput:
def decision_rule(obs: np.ndarray, fcsts: np.ndarray, refs: np.ndarray, context: DecisionContext, parallel_nodes: int) -> MultiParOutput:
outputs = MultiParOutput()
for econ_par in context.economic_model_params:
curr_fcsts = probabilistic_to_deterministic_forecast(fcsts, econ_par)
outputs.insert(econ_par, multiple_timesteps(obs, curr_fcsts, refs, econ_par, context, parallel_nodes))
return outputs

return decision_method
return decision_rule
12 changes: 6 additions & 6 deletions ruv/relative_utility_value.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ def relative_utility_value(obs: np.ndarray, fcsts: np.ndarray, refs: np.ndarray,
decision_thresholds = decision_definition['decision_thresholds']

# decision-making method
decision_making_method = decision_definition['decision_making_method'][0]
decision_making_method_params = decision_definition['decision_making_method'][1]
decision_making_method_fnc = decision_making_method(decision_making_method_params)
decision_rule = decision_definition['decision_rule'][0]
decision_rule_params = decision_definition['decision_rule'][1]
decision_rule_fnc = decision_rule(decision_rule_params)

# damage function
damage_fnc_mth = decision_definition['damage_function'][0]
Expand All @@ -43,7 +43,7 @@ def relative_utility_value(obs: np.ndarray, fcsts: np.ndarray, refs: np.ndarray,
# economic model
economic_model_fnc = decision_definition['economic_model'][0]
economic_model_analytical_spend_fnc = decision_definition['economic_model'][1]
economic_model_params = decision_definition['economic_model'][2] # used by some decision_methods so cant use closure approach
economic_model_params = decision_definition['economic_model'][2]

context_fields = {
'economic_model_params': economic_model_params,
Expand All @@ -52,7 +52,7 @@ def relative_utility_value(obs: np.ndarray, fcsts: np.ndarray, refs: np.ndarray,
'decision_thresholds': decision_thresholds,
'economic_model': economic_model_fnc,
'analytical_spend': economic_model_analytical_spend_fnc,
'decision_making_method': decision_making_method_fnc
'decision_rule': decision_rule_fnc
}
context = DecisionContext(**context_fields)

Expand All @@ -61,7 +61,7 @@ def relative_utility_value(obs: np.ndarray, fcsts: np.ndarray, refs: np.ndarray,
refs = generate_event_freq_ref(obs)

_check_inputs(obs, fcsts, refs, context)
results = context.decision_making_method(obs, fcsts, refs, context, parallel_nodes)
results = context.decision_rule(obs, fcsts, refs, context, parallel_nodes)

return results.to_dict()

Expand Down
30 changes: 15 additions & 15 deletions tests/test_decision_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from ruv.utility_functions import cara
from ruv.damage_functions import logistic_zero
from ruv.helpers import generate_event_freq_ref
from ruv.decision_methods import optimise_over_forecast_distribution, critical_probability_threshold_equals_par, critical_probability_threshold_fixed, critical_probability_threshold_max_value
from ruv.decision_rules import optimise_over_forecast_distribution, critical_probability_threshold_equals_par, critical_probability_threshold_fixed, critical_probability_threshold_max_value


def get_data(ref_equals_fcst=False, event_freq_ref=False):
Expand All @@ -44,15 +44,15 @@ def get_data(ref_equals_fcst=False, event_freq_ref=False):
return obs, fcsts, refs


def get_context(decision_making_method, decision_making_method_params, risk_aversion=0.3):
def get_context(decision_rule, decision_rule_params, risk_aversion=0.3):
context_params = {
'economic_model_params': np.array([0.001, 0.25, 0.5, 0.75, 0.999]),
'utility_function': cara({'A': risk_aversion}),
'damage_function': logistic_zero({'A': 1, 'k': 0.5, 'threshold': 15}),
'decision_thresholds': np.arange(0, 20, 3),
'economic_model': cost_loss,
'analytical_spend': cost_loss_analytical_spend,
'decision_making_method': decision_making_method(decision_making_method_params)
'decision_rule': decision_rule(decision_rule_params)
}
return DecisionContext(**context_params)

Expand All @@ -63,57 +63,57 @@ def test_optimise_over_forecast_distribution():

# basic ensemble fcst and ref
obs, fcsts, refs = get_data()
result = context.decision_making_method(obs, fcsts, refs, context, 1)
result = context.decision_rule(obs, fcsts, refs, context, 1)
assert np.allclose(result.get_series('ruv'), [0.3101, -0.22249, -1.093606, -3.50433, -262.69065], 1e-3)

# event freq ref
obs, fcsts, refs = get_data(event_freq_ref=True)
result = context.decision_making_method(obs, fcsts, refs, context, 1)
result = context.decision_rule(obs, fcsts, refs, context, 1)
assert np.allclose(result.get_series('ruv'), [-34.6425, -0.265098, -1.1084778, -3.67724, -262.69065], 1e-3)

# ref equals fcst
obs, fcsts, refs = get_data(ref_equals_fcst=True)
result = context.decision_making_method(obs, fcsts, refs, context, 1)
result = context.decision_rule(obs, fcsts, refs, context, 1)
assert np.allclose(result.get_series('ruv'), [0, 0, 0, 0, 0], 1e-3)


def test_critical_probability_threshold_equals_par():
obs, fcsts, refs = get_data()

context = get_context(critical_probability_threshold_equals_par, None, 0)
econ_par_result = context.decision_making_method(obs, fcsts, refs, context, 1)
econ_par_result = context.decision_rule(obs, fcsts, refs, context, 1)
context = get_context(optimise_over_forecast_distribution, None, 0)
optim_result = context.decision_making_method(obs, fcsts, refs, context, 1)
optim_result = context.decision_rule(obs, fcsts, refs, context, 1)
assert np.allclose(econ_par_result.get_series('ruv'), optim_result.get_series('ruv'), 1e-3)

context = get_context(critical_probability_threshold_equals_par, None, 0.1)
econ_par_result = context.decision_making_method(obs, fcsts, refs, context, 1)
econ_par_result = context.decision_rule(obs, fcsts, refs, context, 1)
context = get_context(optimise_over_forecast_distribution, None, 0.1)
optim_result = context.decision_making_method(obs, fcsts, refs, context, 1)
optim_result = context.decision_rule(obs, fcsts, refs, context, 1)
assert np.allclose(econ_par_result.get_series('ruv'), optim_result.get_series('ruv'), 1e-3)

context = get_context(critical_probability_threshold_equals_par, None, 5)
econ_par_result = context.decision_making_method(obs, fcsts, refs, context, 1)
econ_par_result = context.decision_rule(obs, fcsts, refs, context, 1)
context = get_context(optimise_over_forecast_distribution, None, 5)
optim_result = context.decision_making_method(obs, fcsts, refs, context, 1)
optim_result = context.decision_rule(obs, fcsts, refs, context, 1)
assert not np.allclose(econ_par_result.get_series('ruv'), optim_result.get_series('ruv'), 1e-3)


def test_critical_probability_threshold_fixed():
obs, fcsts, refs = get_data()
context = get_context(critical_probability_threshold_fixed, {'critical_probability_threshold': 0.5})
result = context.decision_making_method(obs, fcsts, refs, context, 1)
result = context.decision_rule(obs, fcsts, refs, context, 1)
assert np.allclose(result.get_series('ruv'), [0.00398, -0.22249, -1.093606, -3.50433, -1271.97656], 1e-3)


def test_critical_probability_threshold_max_value():
obs, fcsts, refs = get_data()
context = get_context(critical_probability_threshold_max_value, None)
max_result = context.decision_making_method(obs, fcsts, refs, context, 1)
max_result = context.decision_rule(obs, fcsts, refs, context, 1)
assert np.allclose(max_result.get_series('ruv'), [0.00398, 0, -0.18472, -0.66092, -262.6907], 1e-3)

context = get_context(critical_probability_threshold_equals_par, None)
econ_par_result = context.decision_making_method(obs, fcsts, refs, context, 1)
econ_par_result = context.decision_rule(obs, fcsts, refs, context, 1)
assert np.all(max_result.get_series('ruv')[1:] >= econ_par_result.get_series('ruv')[1:]) # ignore first value because economic parameter value is extremely small


4 changes: 2 additions & 2 deletions tests/test_multi_timestep.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from ruv.data_classes import DecisionContext
from ruv.damage_functions import logistic_zero
from ruv.economic_models import cost_loss, cost_loss_analytical_spend
from ruv.decision_methods import optimise_over_forecast_distribution
from ruv.decision_rules import optimise_over_forecast_distribution
from ruv.utility_functions import cara

def get_context():
Expand All @@ -27,7 +27,7 @@ def get_context():
'decision_thresholds': np.arange(0, 20, 1),
'economic_model': cost_loss,
'analytical_spend': cost_loss_analytical_spend,
'decision_making_method': optimise_over_forecast_distribution
'decision_rule': optimise_over_forecast_distribution
}
return DecisionContext(**context_fields)

Expand Down
Loading

0 comments on commit 35feeac

Please sign in to comment.