Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#547: Add tests for new PrescribedPermutation algorithm #548

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 58 additions & 33 deletions src/lbaf/Applications/LBAF_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from lbaf.IO.lbsVTDataReader import LoadReader
from lbaf.IO.lbsVTDataWriter import VTDataWriter
from lbaf.Model.lbsPhase import Phase
from lbaf.Model.lbsWorkModelBase import WorkModelBase
from lbaf.Utils.lbsArgumentParser import PromptArgumentParser
from lbaf.Utils.lbsJSONDataFilesValidatorLoader import \
JSONDataFilesValidatorLoader
Expand Down Expand Up @@ -308,7 +309,7 @@
self.__logger.info(f"Found configuration file at path {path}")
return path

def __print_statistics(self, phase: Phase, phase_name: str):
def __print_statistics(self, phase: Phase, phase_name: str, work_model: WorkModelBase = None):
"""Print a set of rank and edge statistics"""

# Print rank statistics
Expand Down Expand Up @@ -356,8 +357,17 @@
f"{phase_name} sent volumes",
self.__logger)

# Return rank load statistics
return l_stats
if work_model is not None:
w_stats = lbstats.print_function_statistics(
phase.get_ranks(),
work_model.compute,
f"{phase_name} rank work",
self.__logger)
else:
w_stats = None

# Return rank load and work statistics
return l_stats, w_stats

def __print_QOI(self) -> int: # pylint:disable=C0103:invalid-name # not snake case
"""Print list of implemented QOI based on the '-verbosity' command line argument."""
Expand Down Expand Up @@ -404,36 +414,44 @@
for o_qoi in o_qoi_list:
self.__logger.info(f"\t\t{o_qoi}")

def run(self):
def run(self, cfg=None, cfg_dir=None):
"""Run the LBAF application."""
# Parse command line arguments
self.__parse_args()

# Print list of implemented QOI (according to verbosity argument)
self.__print_QOI()

# Warn if default configuration is used because not set as argument
if self.__args.configuration is None:
self.__logger.warning("No configuration file given. Fallback to default `conf.yaml` file in "
"working directory or in the project config directory !")
self.__args.configuration = "conf.yaml"

# Find configuration files
config_file_list = []
# Global configuration (optional)
try:
config_file_list.append(self.__resolve_config_path("global.yaml"))
except FileNotFoundError:
pass
# Local/Specialized configuration (required)
try:
config_file_list.append(self.__resolve_config_path(self.__args.configuration))
except(FileNotFoundError) as err:
self.__logger.error(err)
raise SystemExit(-1) from err

# Apply configuration
cfg = self.__configure(*config_file_list)
# If no configuration was passed directly, look for the file(s)
if cfg is None:
# Parse command line arguments
self.__parse_args()

# Print list of implemented QOI (according to verbosity argument)
self.__print_QOI()

# Warn if default configuration is used because not set as argument
if self.__args.configuration is None:
self.__logger.warning("No configuration file given. Fallback to default `conf.yaml` file in "
"working directory or in the project config directory !")
self.__args.configuration = "conf.yaml"

# Find configuration files
config_file_list = []

# Global configuration (optional)
try:
config_file_list.append(self.__resolve_config_path("global.yaml"))
except FileNotFoundError:
pass

# Local/Specialized configuration (required)
try:
config_file_list.append(self.__resolve_config_path(self.__args.configuration))
except(FileNotFoundError) as err:
self.__logger.error(err)
raise SystemExit(-1) from err

# Apply configuration
cfg = self.__configure(*config_file_list)

else:
self.__parameters = InternalParameters(
config=cfg, base_dir=cfg_dir, logger=self.__logger)

# Download of JSON data files validator required to continue
loader = JSONDataFilesValidatorLoader()
Expand Down Expand Up @@ -577,7 +595,7 @@
self.__logger.info("Calling vt-tv")

# Serialize data to JSON-formatted string
self.__rank_phases = {}

Check warning on line 598 in src/lbaf/Applications/LBAF_app.py

View workflow job for this annotation

GitHub Actions / code-quality (ubuntu-latest, 3.8)

Attribute '__rank_phases' defined outside __init__ (attribute-defined-outside-init)
for p in phases.values():
for r in p.get_ranks():
self.__rank_phases.setdefault(r.get_id(), {})
Expand All @@ -604,14 +622,21 @@

# Report on rebalanced phase when available
if rebalanced_phase:
l_stats = self.__print_statistics(rebalanced_phase, "rebalanced")
l_stats, w_stats = self.__print_statistics(rebalanced_phase, "rebalanced", runtime.get_work_model())
with open(
"imbalance.txt" if self.__parameters.output_dir is None
else os.path.join(
self.__parameters.output_dir,
"imbalance.txt"), 'w', encoding="utf-8") as imbalance_file:
imbalance_file.write(f"{l_stats.get_imbalance()}")

with open(
"w_max.txt" if self.__parameters.output_dir is None
else os.path.join(
self.__parameters.output_dir,
"w_max.txt"), 'w', encoding="utf-8") as w_max_file:
w_max_file.write(f"{w_stats.get_maximum()}")

# If this point is reached everything went fine
self.__logger.info("Process completed without errors")
return 0
Expand Down
107 changes: 107 additions & 0 deletions tests/acceptance/test_permutations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import os
import unittest

from src.lbaf.Applications.LBAF_app import LBAFApplication

class TestPermutations(unittest.TestCase):
"""Class to run acceptance tests"""

def setUp(self):
return

def tearDown(self):
return

def generate_configuration(self, alpha, beta, gamma, permutation):
"""Creates and returns the path to a YAML configuration file."""
# Determine filepaths
acceptance_dir = os.path.dirname(__file__)
test_dir = os.path.dirname(acceptance_dir)
data_dir = os.path.join(os.path.dirname(test_dir), "data")

# Create YAML configuration
config = {
"from_data": {
"data_stem": f"{data_dir}/synthetic_lb_data/data",
"phase_ids": [0],
},
"check_schema": False,
"work_model": {
"name": "AffineCombination",
"parameters": {
"alpha": alpha,
"beta": beta,
"gamma": gamma
}
},
"algorithm": {
"name": "PrescribedPermutation",
"phase_id": 0,
"parameters": {
"permutation": permutation
}
},
"logging_level": "info",
"output_dir": os.path.join(acceptance_dir, "output"),
"output_file_stem": "output_file"
}

# Return the path to the config file
return config

def run_test(self, config, test_case, expected_w_max):
"""Compare LBAF's results to the expected W_max."""
# Determine current directory
acceptance_dir = os.path.dirname(__file__)

# Run LBAF
lbaf = LBAFApplication()
lbaf.run(cfg=config, cfg_dir=acceptance_dir)

# Check w_max file exists
output_dir = os.path.join(acceptance_dir, "output")
imbalance_filepath = os.path.join(output_dir, "imbalance.txt")
w_max_filepath = os.path.join(output_dir, "w_max.txt")
self.assertTrue(os.path.isfile(w_max_filepath), f"File: {w_max_filepath} does not exist!")

# Validate w_max value
with open(w_max_filepath, 'r', encoding="utf-8") as w_max_file:
w_max = float(w_max_file.read())
self.assertEqual(w_max, expected_w_max, f"@@@@@ [{test_case}] FOUND W_MAX: {w_max} @@@@@")

# Clean up
os.remove(w_max_filepath)
os.remove(imbalance_filepath)

def test_ccm_permutation(self):
# Initialize test cases
test_cases = {
"load_only": {
"alpha": 1.0,
"beta": 0.0,
"gamma": 0.0,
"permutation": dict(enumerate([0, 0, 1, 1, 0, 2, 1, 3, 3])),
"W_max": 2.0
},
"off_node_communication_only": {
"alpha": 0.0,
"beta": 1.0,
"gamma": 0.0,
"permutation": dict(enumerate([3, 2, 3, 3, 2, 3, 3, 3, 3])),
"W_max": 0.0
}
}

# Run each test case
for test_case, test_params in test_cases.items():
cfg = self.generate_configuration(
alpha=test_params["alpha"],
beta=test_params["beta"],
gamma=test_params["gamma"],
permutation=test_params["permutation"]
)
self.run_test(cfg, test_case, test_params["W_max"])


if __name__ == "__main__":
unittest.main()
Loading