Skip to content

Commit

Permalink
Ensemble Refactor (#686)
Browse files Browse the repository at this point in the history
Refactor Ensemble parameters

[ committed by @juliaputko ]
[ reviewed by @MattToast, @mellis13  ]
  • Loading branch information
juliaputko authored Sep 5, 2024
1 parent c1faadd commit 0175b6b
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 11 deletions.
5 changes: 3 additions & 2 deletions smartsim/entity/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

from __future__ import annotations

import collections
import copy
import textwrap
import typing as t
Expand Down Expand Up @@ -262,7 +263,7 @@ def _build_exe_args(exe_args: t.Union[str, t.Sequence[str], None]) -> t.List[str
if not (
isinstance(exe_args, str)
or (
isinstance(exe_args, list)
isinstance(exe_args, collections.abc.Sequence)
and all(isinstance(arg, str) for arg in exe_args)
)
):
Expand All @@ -271,7 +272,7 @@ def _build_exe_args(exe_args: t.Union[str, t.Sequence[str], None]) -> t.List[str
if isinstance(exe_args, str):
return exe_args.split()

return exe_args
return list(exe_args)

def print_attached_files(self) -> None:
"""Print a table of the attached files on std out"""
Expand Down
176 changes: 168 additions & 8 deletions smartsim/entity/ensemble.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import os.path
import typing as t

from smartsim.entity import _mock, entity, strategies
from smartsim.entity import entity, strategies
from smartsim.entity.application import Application
from smartsim.entity.files import EntityFiles
from smartsim.entity.strategies import ParamSet
Expand All @@ -59,23 +59,183 @@ def __init__(
max_permutations: int = -1,
replicas: int = 1,
) -> None:
"""Initialize an ``Ensemble`` of application instances
:param name: name of the ensemble
:param exe: executable to run
:param exe_args: executable arguments
:param exe_arg_parameters: parameters and values to be used when configuring entities
:param files: files to be copied, symlinked, and/or configured prior to
execution
:param file_parameters: parameters and values to be used when configuring
files
:param permutation_strategy: strategy to control how the param values are applied to the Ensemble
:param max_permutations: max parameter permutations to set for the ensemble
:param replicas: number of identical entities to create within an Ensemble
"""
self.name = name
self.exe = os.fspath(exe)
"""The name of the ensemble"""
self._exe = os.fspath(exe)
"""The executable to run"""
self.exe_args = list(exe_args) if exe_args else []
self.exe_arg_parameters = (
"""The executable arguments"""
self._exe_arg_parameters = (
copy.deepcopy(exe_arg_parameters) if exe_arg_parameters else {}
)
self.files = copy.deepcopy(files) if files else EntityFiles()
self.file_parameters = dict(file_parameters) if file_parameters else {}
self.permutation_strategy = permutation_strategy
self.max_permutations = max_permutations
self.replicas = replicas
"""The parameters and values to be used when configuring entities"""
self._files = copy.deepcopy(files) if files else EntityFiles()
"""The files to be copied, symlinked, and/or configured prior to execution"""
self._file_parameters = (
copy.deepcopy(file_parameters) if file_parameters else {}
)
"""The parameters and values to be used when configuring files"""
self._permutation_strategy = permutation_strategy
"""The strategy to control how the param values are applied to the Ensemble"""
self._max_permutations = max_permutations
"""The maximum number of entities to come out of the permutation strategy"""
self._replicas = replicas
"""How many identical entities to create within an Ensemble"""

@property
def exe(self) -> str:
"""Return executable to run.
:returns: application executable to run
"""
return self._exe

@exe.setter
def exe(self, value: str | os.PathLike[str]) -> None:
"""Set executable to run.
:param value: executable to run
"""
self._exe = os.fspath(value)

@property
def exe_args(self) -> t.List[str]:
"""Return a list of attached executable arguments.
:returns: application executable arguments
"""
return self._exe_args

@exe_args.setter
def exe_args(self, value: t.Sequence[str]) -> None:
"""Set the executable arguments.
:param value: executable arguments
"""
self._exe_args = list(value)

@property
def exe_arg_parameters(self) -> t.Mapping[str, t.Sequence[t.Sequence[str]]]:
"""Return the executable argument parameters
:returns: executable arguments parameters
"""
return self._exe_arg_parameters

@exe_arg_parameters.setter
def exe_arg_parameters(
self, value: t.Mapping[str, t.Sequence[t.Sequence[str]]]
) -> None:
"""Set the executable arguments.
:param value: executable arguments
"""
self._exe_arg_parameters = copy.deepcopy(value)

@property
def files(self) -> EntityFiles:
"""Return files to be copied, symlinked, and/or configured prior to
execution.
:returns: files
"""
return self._files

@files.setter
def files(self, value: EntityFiles) -> None:
"""Set files to be copied, symlinked, and/or configured prior to
execution.
:param value: files
"""
self._files = copy.deepcopy(value)

@property
def file_parameters(self) -> t.Mapping[str, t.Sequence[str]]:
"""Return file parameters.
:returns: application file parameters
"""
return self._file_parameters

@file_parameters.setter
def file_parameters(self, value: t.Mapping[str, t.Sequence[str]]) -> None:
"""Set the file parameters.
:param value: file parameters
"""
self._file_parameters = dict(value)

@property
def permutation_strategy(self) -> str | strategies.PermutationStrategyType:
"""Return the permutation strategy
:return: permutation strategy
"""
return self._permutation_strategy

@permutation_strategy.setter
def permutation_strategy(
self, value: str | strategies.PermutationStrategyType
) -> None:
"""Set the permutation strategy
:param value: permutation strategy
"""
self._permutation_strategy = value

@property
def max_permutations(self) -> int:
"""Return the maximum permutations
:return: max permutations
"""
return self._max_permutations

@max_permutations.setter
def max_permutations(self, value: int) -> None:
"""Set the maximum permutations
:param value: the maxpermutations
"""
self._max_permutations = value

@property
def replicas(self) -> int:
"""Return the number of replicas
:return: number of replicas
"""
return self._replicas

@replicas.setter
def replicas(self, value: int) -> None:
"""Set the number of replicas
:return: the number of replicas
"""
self._replicas = value

def _create_applications(self) -> tuple[Application, ...]:
"""Concretize the ensemble attributes into a collection of
application instances.
"""
permutation_strategy = strategies.resolve(self.permutation_strategy)

combinations = permutation_strategy(
self.file_parameters, self.exe_arg_parameters, self.max_permutations
)
Expand Down
52 changes: 51 additions & 1 deletion tests/test_ensemble.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@

import itertools
import typing as t
from glob import glob
from os import path as osp

import pytest

from smartsim.entity import _mock
from smartsim.entity.ensemble import Ensemble
from smartsim.entity.files import EntityFiles
from smartsim.entity.strategies import ParamSet
from smartsim.settings.launchSettings import LaunchSettings

Expand All @@ -40,6 +42,54 @@
_2x2_EXE_ARG = {"EXE": [["a"], ["b", "c"]], "ARGS": [["d"], ["e", "f"]]}


@pytest.fixture
def get_gen_configure_dir(fileutils):
yield fileutils.get_test_conf_path(osp.join("generator_files", "tag_dir_template"))


def test_exe_property():
e = Ensemble(name="test", exe="path/to/example_simulation_program")
exe = e.exe
assert exe == e.exe


def test_exe_args_property():
e = Ensemble("test", exe="path/to/example_simulation_program", exe_args="sleepy.py")
exe_args = e.exe_args
assert exe_args == e.exe_args


def test_exe_arg_parameters_property():
exe_arg_parameters = {"-N": 2}
e = Ensemble(
"test",
exe="path/to/example_simulation_program",
exe_arg_parameters=exe_arg_parameters,
)
exe_arg_parameters = e.exe_arg_parameters
assert exe_arg_parameters == e.exe_arg_parameters


def test_files_property(get_gen_configure_dir):
tagged_files = sorted(glob(get_gen_configure_dir + "/*"))
files = EntityFiles(tagged=tagged_files)
e = Ensemble("test", exe="path/to/example_simulation_program", files=files)
files = e.files
assert files == e.files


def test_file_parameters_property():
file_parameters = {"h": [5, 6, 7, 8]}
e = Ensemble(
"test",
exe="path/to/example_simulation_program",
file_parameters=file_parameters,
)
file_parameters = e.file_parameters

assert file_parameters == e.file_parameters


def user_created_function(
file_params: t.Mapping[str, t.Sequence[str]],
exe_arg_params: t.Mapping[str, t.Sequence[t.Sequence[str]]],
Expand Down

0 comments on commit 0175b6b

Please sign in to comment.