-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add more relational constraints, tools, and tests
- Loading branch information
1 parent
5ddcaa0
commit 8fa0c82
Showing
7 changed files
with
201 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import pytest | ||
|
||
from z3 import And, Not, Implies, Or, Solver, sat, unsat | ||
|
||
from ProConPy.config_var import ConfigVar, cvars | ||
from ProConPy.config_var import cvars | ||
from ProConPy.csp_solver import csp | ||
from ProConPy.stage import Stage | ||
|
||
from visualCaseGen.cime_interface import CIME_interface | ||
from visualCaseGen.initialize_configvars import initialize_configvars | ||
from visualCaseGen.initialize_widgets import initialize_widgets | ||
from visualCaseGen.initialize_stages import initialize_stages | ||
from visualCaseGen.specs.options import set_options | ||
from visualCaseGen.specs.relational_constraints import get_relational_constraints | ||
|
||
|
||
def test_initial_satisfiability(): | ||
"""Check that the relational constraints are satisfiable""" | ||
ConfigVar.reboot() | ||
Stage.reboot() | ||
cime = CIME_interface() | ||
initialize_configvars(cime) | ||
initialize_widgets(cime) | ||
initialize_stages(cime) | ||
set_options(cime) | ||
relational_constraints_dict = get_relational_constraints(cvars) | ||
csp.initialize(cvars, relational_constraints_dict, Stage.first()) | ||
|
||
# check that relational constraints are satisfiable | ||
s = Solver() | ||
s.add([k for k in relational_constraints_dict.keys()]) | ||
assert s.check() != unsat, "Relational constraints are not satisfiable." | ||
|
||
# check that initial options are all satisfiable | ||
for varname, var in cvars.items(): | ||
if var.has_options(): | ||
s.add(Or([var == opt for opt in var.options])) | ||
assert s.check() != unsat, f"Initial options for {varname} are not satisfiable." | ||
elif var._options_spec: | ||
opts = var._options_spec() | ||
if opts[0] is not None: | ||
s.add(Or([var == opt for opt in opts])) | ||
assert s.check() != unsat, f"Initial options_spec for {varname} are not satisfiable." | ||
|
||
# check that all initial options are satisfiable in some combination | ||
for varname, var in cvars.items(): | ||
opts = [] | ||
if var.has_options(): | ||
opts = var.options | ||
elif var._options_spec: | ||
opts = var._options_spec()[0] or [] | ||
for opt in opts: | ||
assert s.check(var == opt) == sat, f"Initial option {opt} for {varname} is not satisfiable." | ||
|
||
|
||
def test_constraint_redundancy(): | ||
"""Check to see if any of the relational constraints is redundant | ||
i.e., already implied by the preceding constraints.""" | ||
|
||
ConfigVar.reboot() | ||
Stage.reboot() | ||
cime = CIME_interface() | ||
initialize_configvars(cime) | ||
initialize_widgets(cime) | ||
initialize_stages(cime) | ||
set_options(cime) | ||
relational_constraints_dict = get_relational_constraints(cvars) | ||
csp.initialize(cvars, relational_constraints_dict, Stage.first()) | ||
|
||
constraints = [constr for constr, _ in relational_constraints_dict.items()] | ||
|
||
for i in range(1,len(constraints)): | ||
constraint = constraints[i] | ||
s = Solver() | ||
if s.check(Not(Implies(And(constraints[:i]), constraint))) == unsat: | ||
raise AssertionError(f'Constraint "{constraint}" is redundant.') | ||
|
||
def test_err_msg_repetition(): | ||
"""Check if any error messages are repeated in the relational constraints.""" | ||
|
||
relational_constraints = get_relational_constraints(cvars) | ||
|
||
err_msg_list = [err_msg for _, err_msg in relational_constraints.items()] | ||
err_msg_set = set(err_msg_list) | ||
|
||
# If any error message is repeated, find out which ones are repeated and raise an AssertionError | ||
if len(err_msg_list) != len(err_msg_set): | ||
count = {err_msg: 0 for err_msg in err_msg_set} | ||
for err_msg in err_msg_list: | ||
count[err_msg] += 1 | ||
repeated_err_msgs = {err_msg: count[err_msg] for err_msg in err_msg_set if count[err_msg] > 1} | ||
raise AssertionError(f"Error messages are repeated: {repeated_err_msgs}") | ||
|
||
|
||
if __name__ == "__main__": | ||
test_initial_satisfiability() |
This file was deleted.
Oops, something went wrong.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
#!/usr/bin/env python | ||
|
||
from visualCaseGen.cime_interface import CIME_interface | ||
|
||
cime = CIME_interface() | ||
|
||
def rn(s): | ||
"""Remove numeric characters from a string.""" | ||
return ''.join([i for i in s if not i.isdigit()]) | ||
|
||
|
||
def get_models(compset_lname): | ||
"""Given a compset lname, return the list of models that are coupled.""" | ||
compset_components = cime.get_components_from_compset_lname(compset_lname) | ||
model_list = [] | ||
for comp_class, compset_component in compset_components.items(): | ||
if not compset_component.startswith('X'): | ||
phys = compset_component.split('%')[0] | ||
model = next((model for model in cime.models[comp_class] if phys in cime.comp_phys[model]), None) | ||
model_list.append(model) | ||
return model_list | ||
|
||
def compset_pattern_finder(): | ||
"""Find the models that are never coupled and always coupled with each other. | ||
This is useful for identifying patterns and adding them as relational constraints.""" | ||
|
||
all_models = {model for model_list in cime.models.values() for model in model_list if model[0] != 'x'} | ||
|
||
never = {model: all_models - {model} for model in all_models} | ||
always = {model: all_models - {model} for model in all_models} | ||
|
||
for compset in cime.compsets.values(): | ||
|
||
compset_models = get_models(compset.lname) | ||
|
||
for model in compset_models: | ||
if model: | ||
never[model].difference_update(compset_models) | ||
always[model].intersection_update(compset_models) | ||
|
||
|
||
# From never, remove the models that are in the same component class as the model: | ||
for model, never_coupled in never.items(): | ||
comp_class = None | ||
for cc, models in cime.models.items(): | ||
if model in models: | ||
comp_class = cc | ||
break | ||
if comp_class: | ||
never[model] = never_coupled - set(cime.models[comp_class]) | ||
|
||
# Print the results | ||
print('Never coupled:') | ||
print('----------------------------------------') | ||
for model, never_coupled in never.items(): | ||
if never_coupled: | ||
print(f'{model}: {never_coupled}') | ||
|
||
print('\nAlways coupled:') | ||
print('----------------------------------------') | ||
for model, always_coupled in always.items(): | ||
if always_coupled: | ||
print(f'{model}: {always_coupled}') | ||
|
||
|
||
if __name__ == '__main__': | ||
compset_pattern_finder() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters