diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6aacfcc..ca92f4a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -27,6 +27,11 @@ jobs: with: submodules: recursive + - name: Install system dependencies (Cairo) + run: | + sudo apt-get update + sudo apt-get install -y libcairo2-dev pkg-config + - name: Install Poetry run: pip install poetry==2.1.1 diff --git a/config_examples/config_example_full.json b/config_examples/config_example_full.json deleted file mode 100644 index 226a7ea..0000000 --- a/config_examples/config_example_full.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "generator_path": "", - "alternatives_configuration": { - "alternatives": [ - { - "name": "BBBRVSGenerator", - "params": { - "a": 0.3, - "b": 0.5 - } - } - ], - "sizes": [ - 30, - 40 - ], - "count": 1000, - "threads": 4, - "skip_if_exists": true, - "clear_before": false, - "skip_step": false, - "listeners": [ - "StepListener" - ] - }, - "test_configuration": { - "tests": [ - "AbstractTest" - ], - "worker": "TestWorker", - "hypothesis": "AbstractHypothesis", - "threads": 4, - "listeners": [ - "StepListener" - ], - "skip_step": false - }, - "report_configuration": { - "report_builder": "ReportBuilder", - "data_reader": "", - "listeners": [ - "StepListener" - ] - } -} \ No newline at end of file diff --git a/generators/gen.py b/generators/gen.py deleted file mode 100644 index 6610e9a..0000000 --- a/generators/gen.py +++ /dev/null @@ -1,15 +0,0 @@ -from pysatl_experiment.core.distribution.beta import generate_beta -from pysatl_experiment.experiment.generator import AbstractRVSGenerator - - -class BBBRVSGenerator(AbstractRVSGenerator): - def __init__(self, a, b, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - def code(self): - return super()._convert_to_code(["beta", self.a, self.b]) - - def generate(self, size): - return generate_beta(size=size, a=self.a, b=self.b) diff --git a/graph_norm_experiment.py b/graph_norm_experiment.py deleted file mode 100644 index 6747722..0000000 --- a/graph_norm_experiment.py +++ /dev/null @@ -1,95 +0,0 @@ -import multiprocessing - -from numpy import random as rd - -from pysatl_criterion.statistics.normal import ( - GraphEdgesNumberNormalityGofStatistic, - GraphMaxDegreeNormalityGofStatistic, - KolmogorovSmirnovNormalityGofStatistic, -) -from pysatl_experiment.experiment import Experiment -from pysatl_experiment.experiment.configuration.configuration import ( - AlternativeConfiguration, - ExperimentConfiguration, - ReportConfiguration, - TestConfiguration, -) -from pysatl_experiment.experiment.generator.generators import ( - Chi2Generator, - ExponentialGenerator, - GammaGenerator, - LaplaceRVSGenerator, - WeibullGenerator, -) -from pysatl_experiment.experiment.hypothesis import NormalHypothesis -from pysatl_experiment.experiment.listener.listeners import TimeEstimationListener -from pysatl_experiment.experiment.report.model import PdfPowerReportBuilder -from pysatl_experiment.experiment.test.worker import PowerCalculationWorker -from pysatl_experiment.persistence.db_store import CriticalValueDbStore, ResultDbStore, RvsDbStore - - -if __name__ == "__main__": - print("Start graph normal experiment") - - # Configuring experiment - test_data_tel = TimeEstimationListener() - generate_data_tel = TimeEstimationListener() - - db_url = "sqlite:///graph_norm_experiment_two_sided.sqlite" - listeners = [generate_data_tel] - hypothesis = NormalHypothesis(rd.random() * 10, rd.random() * 30) - test_threads = multiprocessing.cpu_count() - generation_threads = multiprocessing.cpu_count() - sizes = [10, 20, 30, 40, 50] - - critical_value_store = CriticalValueDbStore(db_url=db_url) - rvs_store = RvsDbStore(db_url=db_url) - result_store = ResultDbStore(db_url=db_url) - - alternatives = [ - GammaGenerator(alfa=1, beta=2), - GammaGenerator(alfa=0.5, beta=1), - ExponentialGenerator(2), - WeibullGenerator(a=1, k=2), - Chi2Generator(df=5), - LaplaceRVSGenerator(t=0, s=1), - ] - - edges_two_tailed = GraphEdgesNumberNormalityGofStatistic() - edges_two_tailed.two_tailed = True - - max_degree_two_tailed = GraphMaxDegreeNormalityGofStatistic() - max_degree_two_tailed.two_tailed = True - - tests = [ - edges_two_tailed, - max_degree_two_tailed, - KolmogorovSmirnovNormalityGofStatistic(), - ] - - alternatives_configuration = AlternativeConfiguration( - alternatives, sizes, count=1_000, threads=generation_threads, listeners=listeners - ) - - power_calculation_worker = PowerCalculationWorker(0.05, 1_000_000, critical_value_store, hypothesis=hypothesis) - test_configuration = TestConfiguration( - tests, - threads=test_threads, - worker=power_calculation_worker, - listeners=[test_data_tel], - ) - - report_builder = PdfPowerReportBuilder() - report_configuration = ReportConfiguration(report_builder) - - experiment_configuration = ExperimentConfiguration( - alternatives_configuration, - test_configuration, - report_configuration, - rvs_store=rvs_store, - result_store=result_store, - ) - experiment = Experiment(experiment_configuration) - - # Execute experiment - experiment.execute() diff --git a/pysatl_experiment/experiment/__init__.py b/pysatl_experiment/experiment/__init__.py index 751a9bc..e69de29 100644 --- a/pysatl_experiment/experiment/__init__.py +++ b/pysatl_experiment/experiment/__init__.py @@ -1,4 +0,0 @@ -from pysatl_experiment.experiment.experiment import Experiment - - -__all__ = ["Experiment"] diff --git a/pysatl_experiment/experiment/configuration/__init__.py b/pysatl_experiment/experiment/configuration/__init__.py deleted file mode 100644 index d34c812..0000000 --- a/pysatl_experiment/experiment/configuration/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -from pysatl_experiment.experiment.configuration.configuration import ( - AlternativeConfiguration, - ExperimentConfiguration, - ReportBuilder, - ReportConfiguration, - StepListener, - TestConfiguration, - TestWorker, - TestWorkerResult, -) - - -__all__ = [ - "AlternativeConfiguration", - "ExperimentConfiguration", - "ReportBuilder", - "ReportConfiguration", - "StepListener", - "TestConfiguration", - "TestWorker", - "TestWorkerResult", -] diff --git a/pysatl_experiment/experiment/configuration/config_schema.py b/pysatl_experiment/experiment/configuration/config_schema.py deleted file mode 100644 index a9961f2..0000000 --- a/pysatl_experiment/experiment/configuration/config_schema.py +++ /dev/null @@ -1,24 +0,0 @@ -CONF_SCHEMA = { - "type": "object", - "properties": { - "max_open_trades": { - "description": "Maximum number of open trades. -1 for unlimited.", - "type": ["integer", "number"], - "minimum": -1, - }, - "timeframe": { - "description": ("The timeframe to use (e.g `1m`, `5m`, `15m`, `30m`, `1h` ...)."), - "type": "string", - }, - "stake_currency": { - "description": "Currency used for staking.", - "type": "string", - }, - "stake_amount": { - "description": "Amount to stake per trade.", - "type": ["number", "string"], - "minimum": 0.0001, - "pattern": "", - }, - }, -} diff --git a/pysatl_experiment/experiment/configuration/config_setup.py b/pysatl_experiment/experiment/configuration/config_setup.py deleted file mode 100644 index 6aff2c6..0000000 --- a/pysatl_experiment/experiment/configuration/config_setup.py +++ /dev/null @@ -1,20 +0,0 @@ -import logging -from typing import Any - -from .config_validation import validate_config_consistency -from .load_config import load_from_files - - -logger = logging.getLogger(__name__) - - -def setup_utils_configuration(files: list[str]) -> dict[str, Any]: - """ - Prepare the configuration for utils subcommands - :param files: - :return: Configuration - """ - config = load_from_files(files) - validate_config_consistency(config, preliminary=True) - - return config diff --git a/pysatl_experiment/experiment/configuration/config_validation.py b/pysatl_experiment/experiment/configuration/config_validation.py deleted file mode 100644 index 5043ccb..0000000 --- a/pysatl_experiment/experiment/configuration/config_validation.py +++ /dev/null @@ -1,61 +0,0 @@ -import logging -from copy import deepcopy -from typing import Any - -from jsonschema import Draft4Validator, validators -from jsonschema.exceptions import ValidationError, best_match - -from pysatl_experiment.experiment.configuration.config_schema import CONF_SCHEMA - - -logger = logging.getLogger(__name__) - - -def _extend_validator(validator_class): - """ - Extended validator for the Freqtrade configuration JSON Schema. - Currently, it only handles defaults for subschemas. - """ - validate_properties = validator_class.VALIDATORS["properties"] - - def set_defaults(validator, properties, instance, schema): - for prop, subschema in properties.items(): - if "default" in subschema: - instance.setdefault(prop, subschema["default"]) - - yield from validate_properties(validator, properties, instance, schema) - - return validators.extend(validator_class, {"properties": set_defaults}) - - -FreqtradeValidator = _extend_validator(Draft4Validator) - - -def validate_config_schema(conf: dict[str, Any], preliminary: bool = False) -> dict[str, Any]: - """ - Validate the configuration follow the Config Schema - :param preliminary: preliminary - :param conf: Config in JSON format - :return: Returns the config if valid, otherwise throw an exception - """ - conf_schema = deepcopy(CONF_SCHEMA) - try: - FreqtradeValidator(conf_schema).validate(conf) - return conf - except ValidationError as e: - logger.critical(f"Invalid configuration. Reason: {e}") - raise ValidationError(best_match(Draft4Validator(conf_schema).iter_errors(conf)).message) - - -def validate_config_consistency(conf: dict[str, Any], *, preliminary: bool = False) -> None: - """ - Validate the configuration consistency. - Should be run after loading both configuration and strategy, - since strategies can set certain configuration settings too. - :param conf: Config in JSON format - :return: Returns None if everything is ok, otherwise throw an ConfigurationError - """ - - # validate configuration before returning - logger.info("Validating configuration ...") - validate_config_schema(conf, preliminary=preliminary) diff --git a/pysatl_experiment/experiment/configuration/configuration.py b/pysatl_experiment/experiment/configuration/configuration.py deleted file mode 100644 index a64615c..0000000 --- a/pysatl_experiment/experiment/configuration/configuration.py +++ /dev/null @@ -1,112 +0,0 @@ -from collections.abc import Sequence - -from pysatl_criterion.statistics import AbstractStatistic -from pysatl_experiment.experiment.generator import AbstractRVSGenerator -from pysatl_experiment.persistence import IRvsStore -from pysatl_experiment.persistence.models import IResultStore - - -class TestWorkerResult: - pass - - -class ReportBuilder: - def process(self, data: TestWorkerResult): - pass - - def build(self): - pass - - -class StepListener: - def before(self) -> None: - pass - - def after(self) -> None: - pass - - -class TestWorker: - def init(self): - pass - - def execute(self, test: AbstractStatistic, data: list[list[float]], code, size: int) -> TestWorkerResult: - raise NotImplementedError("Method is not implemented") - - def build_id(self, test: AbstractStatistic, data: list[list[float]], code, size: int) -> str: - raise NotImplementedError("Method is not implemented") - - -class ReportConfiguration: - def __init__(self, report_builder: ReportBuilder, listeners: Sequence[StepListener] | None = None): - """ - Report configuration provides configuration for report. - - :param report_builder: type of report or ReportBuilder - """ - if listeners is None: - listeners = [] - self.report_builder = report_builder - self.listeners = listeners - - -class AlternativeConfiguration: - def __init__( - self, - alternatives: Sequence[AbstractRVSGenerator], - sizes: Sequence[int], - count=1_000, - threads=4, - skip_if_exists: bool = True, - clear_before: bool = False, - skip_step: bool = False, - show_progress: bool = False, - listeners: Sequence[StepListener] | None = None, - ): - if listeners is None: - listeners = [] - self.alternatives = alternatives - self.sizes = sizes - self.count = count - self.threads = threads - self.skip_step = skip_step - self.skip_if_exists = skip_if_exists - self.clear_before = clear_before - self.listeners = listeners - self.show_progress = show_progress - - -class TestConfiguration: - __test__ = False - - def __init__( - self, - tests: Sequence[AbstractStatistic], - worker: TestWorker, - threads=4, - listeners: Sequence[StepListener] | None = None, - skip_step: bool = False, - ): - if listeners is None: - listeners = [] - self.tests = tests - self.threads = threads - self.listeners = listeners - self.worker = worker - self.skip_step = skip_step - - -class ExperimentConfiguration: - def __init__( - self, - alternative_configuration: AlternativeConfiguration, - test_configuration: TestConfiguration, - report_configuration: ReportConfiguration, - rvs_store: IRvsStore, - result_store: IResultStore, - ): - self.alternative_configuration = alternative_configuration - self.test_configuration = test_configuration - self.report_configuration = report_configuration - self.rvs_store = rvs_store - self.result_store = result_store diff --git a/pysatl_experiment/experiment/configuration/load_config.py b/pysatl_experiment/experiment/configuration/load_config.py deleted file mode 100644 index d88eb13..0000000 --- a/pysatl_experiment/experiment/configuration/load_config.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -This module contain functions to load the configuration file -""" - -import logging -import re -import sys -from pathlib import Path -from typing import Any - -import rapidjson - -from pysatl_experiment.constants import Config -from pysatl_experiment.exceptions import ConfigurationError, OperationalException -from pysatl_experiment.misc import deep_merge_dicts - - -logger = logging.getLogger(__name__) - -CONFIG_PARSE_MODE = rapidjson.PM_COMMENTS | rapidjson.PM_TRAILING_COMMAS - - -def log_config_error_range(path: str, errmsg: str) -> str: - """ - Parses configuration file and prints range around error - """ - if path != "-": - offsetlist = re.findall(r"(?<=Parse\serror\sat\soffset\s)\d+", errmsg) - if offsetlist: - offset = int(offsetlist[0]) - text = Path(path).read_text() - # Fetch an offset of 80 characters around the error line - subtext = text[offset - min(80, offset) : offset + 80] - segments = subtext.split("\n") - if len(segments) > 3: - # Remove first and last lines, to avoid odd truncations - return "\n".join(segments[1:-1]) - else: - return subtext - return "" - - -def load_config_file(path: str) -> dict[str, Any]: - """ - Loads a config file from the given path - :param path: path as str - :return: configuration as dictionary - """ - try: - # Read config from stdin if requested in the options - with Path(path).open() if path != "-" else sys.stdin as file: - config = rapidjson.load(file, parse_mode=CONFIG_PARSE_MODE) - except FileNotFoundError: - raise OperationalException( - f'Config file "{path}" not found!\nPlease, create a config file or check whether it exists.' - ) from None - except rapidjson.JSONDecodeError as e: - err_range = log_config_error_range(path, str(e)) - raise ConfigurationError( - f"{e}\nPlease verify the following segment of your configuration:\n{err_range}" - if err_range - else "Please verify your configuration file for syntax errors." - ) - - return config - - -def load_from_files(files: list[str], base_path: Path | None = None, level: int = 0) -> dict[str, Any]: - """ - Recursively load configuration files if specified. - Sub-files are assumed to be relative to the initial config. - """ - config: Config = {} - if level > 5: - raise ConfigurationError("Config loop detected.") - - if not files: - raise ConfigurationError("No configuration file found.") - files_loaded = [] - # We expect here a list of config filenames - for filename in files: - logger.info(f"Using config: {filename} ...") - if filename == "-": - # Immediately load stdin and return - return load_config_file(filename) - file = Path(filename) - if base_path: - # Prepend base path to allow for relative assignments - file = base_path / file - - config_tmp = load_config_file(str(file)) - if "add_config_files" in config_tmp: - config_sub = load_from_files(config_tmp["add_config_files"], file.resolve().parent, level + 1) - files_loaded.extend(config_sub.get("config_files", [])) - config_tmp = deep_merge_dicts(config_tmp, config_sub) - - files_loaded.insert(0, str(file)) - - # Merge config options, overwriting prior values - config = deep_merge_dicts(config_tmp, config) - - config["config_files"] = files_loaded - - return config diff --git a/pysatl_experiment/experiment/experiment.py b/pysatl_experiment/experiment/experiment.py deleted file mode 100644 index ac6d6ca..0000000 --- a/pysatl_experiment/experiment/experiment.py +++ /dev/null @@ -1,32 +0,0 @@ -from pysatl_experiment.experiment.configuration.configuration import ExperimentConfiguration -from pysatl_experiment.experiment.generator.generator_step import data_generation_step -from pysatl_experiment.experiment.report.report_step import execute_report_step -from pysatl_experiment.experiment.test.test_step import execute_test_step - - -class Experiment: - def __init__(self, configuration: ExperimentConfiguration): - self.__configuration = configuration - - def execute(self): - """ - - Execute experiment. - - """ - - rvs_store = self.__configuration.rvs_store - result_store = self.__configuration.result_store - rvs_store.init() - - worker = self.__configuration.test_configuration.worker - worker.init() - - # Generate data for alternatives - data_generation_step(self.__configuration.alternative_configuration, rvs_store) - - # Test hypothesis - execute_test_step(self.__configuration.test_configuration, rvs_store, result_store) - - # Generate reports - execute_report_step(self.__configuration.report_configuration, result_store) diff --git a/pysatl_experiment/experiment_new/experiment/experiment.py b/pysatl_experiment/experiment/experiment/experiment.py similarity index 100% rename from pysatl_experiment/experiment_new/experiment/experiment.py rename to pysatl_experiment/experiment/experiment/experiment.py diff --git a/pysatl_experiment/experiment_new/experiment_steps/experiment_steps.py b/pysatl_experiment/experiment/experiment_steps/experiment_steps.py similarity index 100% rename from pysatl_experiment/experiment_new/experiment_steps/experiment_steps.py rename to pysatl_experiment/experiment/experiment_steps/experiment_steps.py diff --git a/pysatl_experiment/experiment/generator/__init__.py b/pysatl_experiment/experiment/generator/__init__.py deleted file mode 100644 index 1302594..0000000 --- a/pysatl_experiment/experiment/generator/__init__.py +++ /dev/null @@ -1,100 +0,0 @@ -from pysatl_experiment.experiment.generator.generators import ( - BetaRVSGenerator, - CauchyRVSGenerator, - Chi2Generator, - GammaGenerator, - GumbelGenerator, - LaplaceRVSGenerator, - LoConNormGenerator, - LogisticRVSGenerator, - LognormGenerator, - MixConNormGenerator, - ScConNormGenerator, - TruncnormGenerator, - TRVSGenerator, - TukeyRVSGenerator, - WeibullGenerator, -) -from pysatl_experiment.experiment.generator.model import AbstractRVSGenerator - - -symmetric_generators = [ - BetaRVSGenerator(a=0.5, b=0.5), - BetaRVSGenerator(a=1, b=1), - BetaRVSGenerator(a=2, b=2), - CauchyRVSGenerator(t=0, s=0.5), - CauchyRVSGenerator(t=0, s=1), - CauchyRVSGenerator(t=0, s=2), - LaplaceRVSGenerator(t=0, s=1), - LogisticRVSGenerator(t=2, s=2), - TRVSGenerator(df=1), - TRVSGenerator(df=2), - TRVSGenerator(df=4), - TRVSGenerator(df=10), - TukeyRVSGenerator(lam=0.14), - TukeyRVSGenerator(lam=0.5), - TukeyRVSGenerator(lam=2), - TukeyRVSGenerator(lam=5), - TukeyRVSGenerator(lam=10), -] -asymmetric_generators = [ - BetaRVSGenerator(a=2, b=1), - BetaRVSGenerator(a=2, b=5), - BetaRVSGenerator(a=4, b=0.5), - BetaRVSGenerator(a=5, b=1), - Chi2Generator(df=1), - Chi2Generator(df=2), - Chi2Generator(df=4), - Chi2Generator(df=10), - GammaGenerator(alfa=2, beta=2), - GammaGenerator(alfa=3, beta=2), - GammaGenerator(alfa=5, beta=1), - GammaGenerator(alfa=9, beta=1), - GammaGenerator(alfa=15, beta=1), - GammaGenerator(alfa=100, beta=1), - GumbelGenerator(mu=1, beta=2), - LognormGenerator(s=1, mu=0), - WeibullGenerator(a=0.5, k=1), - WeibullGenerator(a=1, k=2), - WeibullGenerator(a=2, k=3.4), - WeibullGenerator(a=3, k=4), -] -modified_generators = [ - TruncnormGenerator(a=-1, b=1), - TruncnormGenerator(a=-2, b=2), - TruncnormGenerator(a=-3, b=3), - TruncnormGenerator(a=-3, b=1), - TruncnormGenerator(a=-3, b=2), - LoConNormGenerator(p=0.3, a=1), - LoConNormGenerator(p=0.4, a=1), - LoConNormGenerator(p=0.5, a=1), - LoConNormGenerator(p=0.3, a=3), - LoConNormGenerator(p=0.4, a=3), - LoConNormGenerator(p=0.5, a=3), - LoConNormGenerator(p=0.3, a=5), - LoConNormGenerator(p=0.4, a=5), - LoConNormGenerator(p=0.5, a=5), - ScConNormGenerator(p=0.05, b=0.25), - ScConNormGenerator(p=0.10, b=0.25), - ScConNormGenerator(p=0.20, b=0.25), - ScConNormGenerator(p=0.05, b=2), - ScConNormGenerator(p=0.10, b=2), - ScConNormGenerator(p=0.20, b=2), - ScConNormGenerator(p=0.05, b=4), - ScConNormGenerator(p=0.10, b=4), - ScConNormGenerator(p=0.20, b=4), - MixConNormGenerator(p=0.3, a=1, b=0.25), - MixConNormGenerator(p=0.4, a=1, b=0.25), - MixConNormGenerator(p=0.5, a=1, b=0.25), - MixConNormGenerator(p=0.3, a=3, b=0.25), - MixConNormGenerator(p=0.4, a=3, b=0.25), - MixConNormGenerator(p=0.5, a=3, b=0.25), - MixConNormGenerator(p=0.3, a=1, b=4), - MixConNormGenerator(p=0.4, a=1, b=4), - MixConNormGenerator(p=0.5, a=1, b=4), - MixConNormGenerator(p=0.3, a=3, b=4), - MixConNormGenerator(p=0.4, a=3, b=4), - MixConNormGenerator(p=0.5, a=3, b=4), -] - -__all__ = ["AbstractRVSGenerator"] diff --git a/pysatl_experiment/experiment/generator/generator_step.py b/pysatl_experiment/experiment/generator/generator_step.py deleted file mode 100644 index 2cd66f3..0000000 --- a/pysatl_experiment/experiment/generator/generator_step.py +++ /dev/null @@ -1,116 +0,0 @@ -import logging -from multiprocessing import Queue -from multiprocessing.synchronize import Event as EventClass - -from pysatl_experiment.experiment.configuration.configuration import AlternativeConfiguration -from pysatl_experiment.experiment.generator import AbstractRVSGenerator -from pysatl_experiment.experiment.pipeline import start_pipeline -from pysatl_experiment.persistence.models import IRvsStore - - -logger = logging.getLogger(__name__) - - -def generate_rvs_data(rvs_generator: AbstractRVSGenerator, size, count): - """ - Generate data rvs - - :param rvs_generator: generator to generate rvs data - :param size: size of rvs vector - :param count: rvs count - :return: Data Frame, where rows is rvs - """ - - return [rvs_generator.generate(size) for _ in range(count)] - - -def process_entries( - generate_queue: Queue, - info_queue: Queue, - generate_shutdown_event: EventClass, - info_shutdown_event: EventClass, - kwargs, -): - store = kwargs["store"] - store.init() - - while not (generate_shutdown_event.is_set() and generate_queue.empty()): - if not generate_queue.empty(): - generator, size, count = generate_queue.get() - data = generate_rvs_data(generator, size, count) - store.insert_all_rvs(generator.code(), size, data) - info_queue.put(1) - - info_shutdown_event.set() - - -def fill_queue( - queue, - generate_shutdown_event, - kwargs, -): - sizes = kwargs["sizes"] - count = kwargs["count"] - store: IRvsStore = kwargs["store"] - rvs_generators: list[AbstractRVSGenerator] | None = kwargs["rvs_generators"] - - store.init() - - for size in sizes: - for generator in rvs_generators: - try: - code = generator.code() - data_count = store.get_rvs_count(code, size) - if data_count < count: - count = count - data_count - queue.put((generator, size, count)) - except Exception as e: - logger.warning(f"Error on generation ${generator.code()} with size ${size}", e) - - generate_shutdown_event.set() - - -def data_generation_step(alternative_configuration: AlternativeConfiguration, store: IRvsStore): - """ - - Generate data and save it to store. - - :param alternative_configuration: alternative configuration - :param store: RVS store - """ - - # Skip step - if alternative_configuration.skip_step: - logger.info("Skip data generation step") - return - - logger.info("Start data generation step") - # Execute before all listeners - for listener in alternative_configuration.listeners: - listener.before() - - # Clear all data - if alternative_configuration.clear_before: - store.clear_all_rvs() - - threads_count = alternative_configuration.threads - rvs_generators = alternative_configuration.alternatives - sizes = alternative_configuration.sizes - - start_pipeline( - fill_queue, - process_entries, - threads_count, - total_count=len(sizes) * len(rvs_generators), - queue_size=2000, - sizes=sizes, - count=alternative_configuration.count, - rvs_generators=rvs_generators, - store=store, - ) - - # Execute after all listeners - for listener in alternative_configuration.listeners: - listener.after() - - logger.info("End data generation step") diff --git a/pysatl_experiment/experiment/generator/generators.py b/pysatl_experiment/experiment/generator/generators.py deleted file mode 100644 index f752517..0000000 --- a/pysatl_experiment/experiment/generator/generators.py +++ /dev/null @@ -1,322 +0,0 @@ -from typing_extensions import override - -from pysatl_experiment.core.distribution.beta import generate_beta -from pysatl_experiment.core.distribution.cauchy import generate_cauchy -from pysatl_experiment.core.distribution.chi2 import generate_chi2 -from pysatl_experiment.core.distribution.expon import generate_expon -from pysatl_experiment.core.distribution.gamma import generate_gamma -from pysatl_experiment.core.distribution.gompertz import generate_gompertz -from pysatl_experiment.core.distribution.gumbel import generate_gumbel -from pysatl_experiment.core.distribution.invgauss import generate_invgauss -from pysatl_experiment.core.distribution.laplace import generate_laplace -from pysatl_experiment.core.distribution.lo_con_norm import generate_lo_con_norm -from pysatl_experiment.core.distribution.logistic import generate_logistic -from pysatl_experiment.core.distribution.lognormal import generate_lognorm -from pysatl_experiment.core.distribution.mix_con_norm import generate_mix_con_norm -from pysatl_experiment.core.distribution.norm import generate_norm -from pysatl_experiment.core.distribution.rice import generate_rice -from pysatl_experiment.core.distribution.scale_con_norm import generate_scale_con_norm -from pysatl_experiment.core.distribution.student import generate_t -from pysatl_experiment.core.distribution.truncnormal import generate_truncnorm -from pysatl_experiment.core.distribution.tukey import generate_tukey -from pysatl_experiment.core.distribution.weibull import generate_weibull -from pysatl_experiment.experiment.generator.model import AbstractRVSGenerator - - -class BetaRVSGenerator(AbstractRVSGenerator): - def __init__(self, a, b, **kwargs): - super().__init__(**kwargs) - self.a = a - self.b = b - - @override - def code(self): - return super()._convert_to_code(["beta", self.a, self.b]) - - @override - def generate(self, size): - return generate_beta(size=size, a=self.a, b=self.b) - - -class CauchyRVSGenerator(AbstractRVSGenerator): - def __init__(self, t, s, **kwargs): - super().__init__(**kwargs) - self.t = t - self.s = s - - @override - def code(self): - return super()._convert_to_code(["cauchy", self.t, self.s]) - - @override - def generate(self, size): - return generate_cauchy(size=size, t=self.t, s=self.s) - - -class LaplaceRVSGenerator(AbstractRVSGenerator): - def __init__(self, t, s, **kwargs): - super().__init__(**kwargs) - self.t = t - self.s = s - - @override - def code(self): - return super()._convert_to_code(["laplace", self.t, self.s]) - - @override - def generate(self, size): - return generate_laplace(size=size, t=self.t, s=self.s) - - -class LogisticRVSGenerator(AbstractRVSGenerator): - def __init__(self, t, s, **kwargs): - super().__init__(**kwargs) - self.t = t - self.s = s - - @override - def code(self): - return super()._convert_to_code(["logistic", self.t, self.s]) - - @override - def generate(self, size): - return generate_logistic(size=size, t=self.t, s=self.s) - - -class TRVSGenerator(AbstractRVSGenerator): - def __init__(self, df, **kwargs): - super().__init__(**kwargs) - self.df = df - - @override - def code(self): - return super()._convert_to_code(["student", self.df]) - - @override - def generate(self, size): - return generate_t(size=size, df=self.df) - - -class TukeyRVSGenerator(AbstractRVSGenerator): - def __init__(self, lam, **kwargs): - super().__init__(**kwargs) - self.lam = lam - - @override - def code(self): - return super()._convert_to_code(["tukey", self.lam]) - - @override - def generate(self, size): - return generate_tukey(size=size, lam=self.lam) - - -class LognormGenerator(AbstractRVSGenerator): - def __init__(self, s=1, mu=0, **kwargs): - super().__init__(**kwargs) - self.s = s - self.mu = mu - - @override - def code(self): - return super()._convert_to_code(["lognorm", self.s, self.mu]) - - @override - def generate(self, size): - return generate_lognorm(size=size, s=self.s, mu=self.mu) - - -class GammaGenerator(AbstractRVSGenerator): - def __init__(self, alfa=1, beta=0, **kwargs): - super().__init__(**kwargs) - self.alfa = alfa - self.beta = beta - - @override - def code(self): - return super()._convert_to_code(["gamma", self.alfa, self.beta]) - - @override - def generate(self, size): - return generate_gamma(size=size, alfa=self.alfa, beta=self.beta) - - -class TruncnormGenerator(AbstractRVSGenerator): - def __init__(self, mean=0, var=1, a=-10, b=10, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.var = var - self.a = a - self.b = b - - @override - def code(self): - return super()._convert_to_code(["truncnorm", self.mean, self.var, self.a, self.b]) - - @override - def generate(self, size): - return generate_truncnorm(size=size, mean=self.mean, var=self.var, a=self.a, b=self.b) - - -class Chi2Generator(AbstractRVSGenerator): - def __init__(self, df=2, **kwargs): - super().__init__(**kwargs) - self.df = df - - @override - def code(self): - return super()._convert_to_code(["chi2", self.df]) - - @override - def generate(self, size): - return generate_chi2(size=size, df=self.df) - - -class GumbelGenerator(AbstractRVSGenerator): - def __init__(self, mu=0, beta=1, **kwargs): - super().__init__(**kwargs) - self.mu = mu - self.beta = beta - - @override - def code(self): - return super()._convert_to_code(["gumbel", self.mu, self.beta]) - - @override - def generate(self, size): - return generate_gumbel(size=size, mu=self.mu, beta=self.beta) - - -class WeibullGenerator(AbstractRVSGenerator): - def __init__(self, a=1, k=5, **kwargs): - super().__init__(**kwargs) - self.a = a - self.k = k - - @override - def code(self): - return super()._convert_to_code(["weibull", self.a, self.k]) - - @override - def generate(self, size): - return generate_weibull(size=size, a=self.a, k=self.k) - - -class LoConNormGenerator(AbstractRVSGenerator): - def __init__(self, p=0.5, a=0, **kwargs): - super().__init__(**kwargs) - self.p = p - self.a = a - - @override - def code(self): - return super()._convert_to_code(["lo_con_norm", self.p, self.a]) - - @override - def generate(self, size): - return generate_lo_con_norm(size=size, p=self.p, a=self.a) - - -class ScConNormGenerator(AbstractRVSGenerator): - def __init__(self, p=0.5, b=1, **kwargs): - super().__init__(**kwargs) - self.p = p - self.b = b - - @override - def code(self): - return super()._convert_to_code(["scale_con_norm", self.p, self.b]) - - @override - def generate(self, size): - return generate_scale_con_norm(size=size, p=self.p, b=self.b) - - -class MixConNormGenerator(AbstractRVSGenerator): - def __init__(self, p=0.5, a=0, b=1, **kwargs): - super().__init__(**kwargs) - self.p = p - self.a = a - self.b = b - - @override - def code(self): - return super()._convert_to_code(["mix_con_norm", self.p, self.a, self.b]) - - @override - def generate(self, size): - return generate_mix_con_norm(size=size, p=self.p, a=self.a, b=self.b) - - -class ExponentialGenerator(AbstractRVSGenerator): - def __init__(self, lam=0.5, **kwargs): - super().__init__(**kwargs) - self.lam = lam - - @override - def code(self): - return super()._convert_to_code(["exponential", self.lam]) - - @override - def generate(self, size): - return generate_expon(size=size, lam=self.lam) - - -class InvGaussGenerator(AbstractRVSGenerator): - def __init__(self, mu=0, lam=1, **kwargs): - super().__init__(**kwargs) - self.mu = mu - self.lam = lam - - @override - def code(self): - return super()._convert_to_code(["invgauss", self.mu, self.lam]) - - @override - def generate(self, size): - return generate_invgauss(size=size, mu=self.mu, lam=self.lam) - - -class RiceGenerator(AbstractRVSGenerator): - def __init__(self, nu=0, sigma=1, **kwargs): - super().__init__(**kwargs) - self.nu = nu - self.sigma = sigma - - @override - def code(self): - return super()._convert_to_code(["rice", self.nu, self.sigma]) - - @override - def generate(self, size): - return generate_rice(size=size, nu=self.nu, sigma=self.sigma) - - -class GompertzGenerator(AbstractRVSGenerator): - def __init__(self, eta=0, b=1, **kwargs): - super().__init__(**kwargs) - self.eta = eta - self.b = b - - @override - def code(self): - return super()._convert_to_code(["gompertz", self.eta, self.b]) - - @override - def generate(self, size): - return generate_gompertz(size=size, eta=self.eta, b=self.b) - - -class NormalGenerator(AbstractRVSGenerator): - def __init__(self, mean=0, var=1, **kwargs): - super().__init__(**kwargs) - self.mean = mean - self.var = var - - @override - def code(self): - return super()._convert_to_code(["normal", self.mean, self.var]) - - @override - def generate(self, size): - return generate_norm(size=size, mean=self.mean, var=self.var) diff --git a/pysatl_experiment/experiment/generator/model.py b/pysatl_experiment/experiment/generator/model.py deleted file mode 100644 index a945e82..0000000 --- a/pysatl_experiment/experiment/generator/model.py +++ /dev/null @@ -1,14 +0,0 @@ -class AbstractRVSGenerator: - def __init__(self, **kwargs): - pass - - def code(self): - return NotImplementedError("Method is not implemented") - - @staticmethod - def _convert_to_code(items: list): - return "_".join(str(x) for x in items) - - @staticmethod - def generate(size): - raise NotImplementedError("Method is not implemented") diff --git a/pysatl_experiment/experiment/hypothesis/__init__.py b/pysatl_experiment/experiment/hypothesis/__init__.py deleted file mode 100644 index cc92580..0000000 --- a/pysatl_experiment/experiment/hypothesis/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from pysatl_experiment.experiment.hypothesis.hypothesis import NormalHypothesis, WeibullHypothesis -from pysatl_experiment.experiment.hypothesis.model import AbstractHypothesis - - -__all__ = ["NormalHypothesis", "WeibullHypothesis", "AbstractHypothesis"] diff --git a/pysatl_experiment/experiment/hypothesis/hypothesis.py b/pysatl_experiment/experiment/hypothesis/hypothesis.py deleted file mode 100644 index 6af1e9b..0000000 --- a/pysatl_experiment/experiment/hypothesis/hypothesis.py +++ /dev/null @@ -1,20 +0,0 @@ -from pysatl_experiment.core.distribution import norm, weibull -from pysatl_experiment.experiment.hypothesis.model import AbstractHypothesis - - -class NormalHypothesis(AbstractHypothesis): - def __init__(self, mean=0, var=1): - self.mean = mean - self.var = var - - def generate(self, size, **kwargs): - return norm.generate_norm(size, self.mean, self.var) - - -class WeibullHypothesis(AbstractHypothesis): - def __init__(self, a=1, k=5): - self.a = a - self.k = k - - def generate(self, size, **kwargs): - return weibull.generate_weibull(size, self.a, self.k) diff --git a/pysatl_experiment/experiment/hypothesis/model.py b/pysatl_experiment/experiment/hypothesis/model.py deleted file mode 100644 index 3918e5a..0000000 --- a/pysatl_experiment/experiment/hypothesis/model.py +++ /dev/null @@ -1,7 +0,0 @@ -from abc import ABC, abstractmethod - - -class AbstractHypothesis(ABC): - @abstractmethod - def generate(self, size, **kwargs): - raise NotImplementedError("Method is not implemented") diff --git a/pysatl_experiment/experiment/listener/listeners.py b/pysatl_experiment/experiment/listener/listeners.py deleted file mode 100644 index d3506e2..0000000 --- a/pysatl_experiment/experiment/listener/listeners.py +++ /dev/null @@ -1,16 +0,0 @@ -import timeit - -from pysatl_experiment.experiment.configuration.configuration import StepListener - - -class TimeEstimationListener(StepListener): - def __init__(self): - self.start_time = None - self.end_time = None - - def before(self) -> None: - self.start_time = timeit.default_timer() - - def after(self) -> None: - self.end_time = timeit.default_timer() - print("Generation time (s)", self.end_time - self.start_time) diff --git a/pysatl_experiment/experiment/listener/model.py b/pysatl_experiment/experiment/listener/model.py deleted file mode 100644 index e69de29..0000000 diff --git a/pysatl_experiment/experiment_new/model/experiment_step/experiment_step.py b/pysatl_experiment/experiment/model/experiment_step/experiment_step.py similarity index 100% rename from pysatl_experiment/experiment_new/model/experiment_step/experiment_step.py rename to pysatl_experiment/experiment/model/experiment_step/experiment_step.py diff --git a/pysatl_experiment/experiment/pipeline.py b/pysatl_experiment/experiment/pipeline.py deleted file mode 100644 index 3d891be..0000000 --- a/pysatl_experiment/experiment/pipeline.py +++ /dev/null @@ -1,48 +0,0 @@ -from multiprocessing import Event, Manager, Process, Queue - -from tqdm import tqdm - - -def __show_prog(queue: Queue, shutdown_event, total): - prog = tqdm(total=total, desc="Processing data", unit_scale=True) - while not (shutdown_event.is_set() and queue.empty()): - try: - to_add = queue.get(timeout=1) - prog.update(to_add) - if prog.n >= total: - break - except: # noqa: E722, S110 - pass - prog.update(total - prog.n) - - -def start_pipeline(fill_queue, process_entries, num_workers, total_count=0, queue_size=2000, **kwargs): - queue_manager = Manager() - queue = queue_manager.Queue(maxsize=queue_size) - info_queue = queue_manager.Queue(maxsize=2000) - shutdown_event = Event() - info_shutdown_event = Event() - - process_fill_queue = Process( - target=fill_queue, - args=(queue, shutdown_event, kwargs), - ) - process_fill_queue.start() - - processes = [] - for p in range(num_workers): - p = Process( - target=process_entries, - args=(queue, info_queue, shutdown_event, info_shutdown_event, kwargs), - ) - p.start() - processes.append(p) - - if total_count > 0: - progress = Process(target=__show_prog, args=(info_queue, info_shutdown_event, total_count)) - progress.start() - progress.join() - - process_fill_queue.join() - for p in processes: - p.join() diff --git a/pysatl_experiment/experiment/report/model.py b/pysatl_experiment/experiment/report/model.py deleted file mode 100644 index 13cca77..0000000 --- a/pysatl_experiment/experiment/report/model.py +++ /dev/null @@ -1,202 +0,0 @@ -from typing import Any - -from fpdf import FPDF -from matplotlib import pyplot as plt - -from pysatl_experiment.experiment.configuration import TestWorkerResult -from pysatl_experiment.experiment.configuration.configuration import ReportBuilder -from pysatl_experiment.experiment.test.worker import PowerWorkerResult -from pysatl_experiment.persistence.models import IResultStore - - -""" -class ChartBenchmarkMeanReportBuilder(ReportBuilder): - def __init__(self): - self.data = {} - self.sizes = set() - self.codes = set() - - def process(self, result: BenchmarkWorkerResult): - key = result.test_code # ChartBenchmarkMeanReportBuilder.__build_path(result) - point = (result.size, np.mean(result.benchmark)) - self.sizes.add(result.size) - self.codes.add(result.test_code) - if key in self.data.keys(): - self.data[key].append(point) - else: - self.data[key] = [point] - - def build(self): - sizes = [f"{i}" for i in sorted(self.sizes)] - x = np.arange(len(sizes)) - width = 0.1 - fig, ax = plt.subplots() - i = 1 - for key in self.data: - value = self.data[key] - sorted_value = sorted(value, key=lambda tup: tup[0]) - p = [x[1] for x in sorted_value] - - ax.bar(x + i * width, p, width, label=key) - i += 1 - ax.set_title('Пример групповой диаграммы') - ax.set_xticks(x) - ax.set_xticklabels(sizes) - ax.legend() - plt.show() - - @staticmethod - def __build_path(result: BenchmarkWorkerResult): - return '_'.join([result.test_code, str(result.size)]) -""" - - -class ChartPowerReportBuilder(ReportBuilder): - def __init__(self): - self.data = {} - - def process(self, result: TestWorkerResult): - if not isinstance(result, PowerWorkerResult): - raise TypeError(f"Type {type(result)} is not instance of PowerWorkerResult") - - key = ChartPowerReportBuilder.__build_path(result) - point = (result.size, result.power) - if key in self.data.keys(): - self.data[key].append(point) - else: - self.data[key] = [point] - - def build(self): - for key in self.data: - value = self.data[key] - sorted_value = sorted(value, key=lambda tup: tup[0]) - s = [x[0] for x in sorted_value] - p = [x[1] for x in sorted_value] - - fig, ax = plt.subplots() - ax.plot(s, p) - - ax.set( - xlabel="time (s)", - ylabel="voltage (mV)", - title="About as simple as it gets, folks", - ) - ax.grid() - - fig.savefig("test.png") - plt.show() - - @staticmethod - def __build_path(result: PowerWorkerResult): - return "_".join([result.test_code, str(result.alternative_code), str(result.alpha)]) - - -class PdfPowerReportBuilder(ReportBuilder): - def __init__(self): - self.data = {} - self.sizes = set() - self.tests = set() - self.font = "helvetica" - self.border = 1 - self.align = "C" - self.col_width = 30 - self.header_font_size = 12 - self.entry_font_size = 10 - self.output_filename = "power_report.pdf" - - def process(self, result: TestWorkerResult): - if not isinstance(result, PowerWorkerResult): - raise TypeError(f"Type {type(result)} is not an instance of PowerWorkerResult") - - key = PdfPowerReportBuilder.__build_path(result) - self.sizes.add(result.size) - self.tests.add(result.test_code) - - if key not in self.data: - self.data[key] = {} - - if result.test_code not in self.data[key]: - self.data[key][result.test_code] = {} - - self.data[key][result.test_code][result.size] = result.power - - def build(self): - pdf = FPDF(orientation="L") - pdf.set_auto_page_break(auto=True, margin=15) - pdf.add_page() - pdf.set_font(self.font, size=self.entry_font_size) - - sorted_sizes = sorted(self.sizes) - sorted_tests = sorted(self.tests) - table_width = (len(sorted_sizes) + 1) * self.col_width - margin_x = (pdf.w - table_width) / 2 - - for key, results in self.data.items(): - pdf.set_font(self.font, "B", self.header_font_size) - pdf.cell(0, self.entry_font_size, f"{key}", ln=True, align="C") - pdf.ln(5) - - pdf.set_x(margin_x) - pdf.set_font(self.font, "B", self.entry_font_size) - pdf.cell(self.col_width, self.entry_font_size, "Test", border=self.border, align=self.align) - for size in sorted_sizes: - pdf.cell( - self.col_width, - self.entry_font_size, - str(size), - border=self.border, - align=self.align, - ) - pdf.ln() - - pdf.set_font(self.font, size=self.entry_font_size) - for test in sorted_tests: - test_name = test.split("_")[0] - pdf.set_x(margin_x) - pdf.cell( - self.col_width, - self.entry_font_size, - test_name, - border=self.border, - align=self.align, - ) - for size in sorted_sizes: - power = results.get(test, {}).get(size, "N/A") - pdf.cell( - self.col_width, - self.entry_font_size, - f"{power:.3f}" if isinstance(power, float) else str(power), - border=self.border, - align=self.align, - ) - pdf.ln() - pdf.ln(self.entry_font_size) - - pdf.output(self.output_filename) - print(f"PDF report saved as: {self.output_filename}") - - @staticmethod - def __build_path(result: PowerWorkerResult): - return f"Alternative: {result.alternative_code} alpha: {result.alpha}" - - -class ResultReader: - def __init__(self, result_store: IResultStore, batch_size=100): - self.result_store = result_store - self.batch_size = batch_size - self.offset = 0 - self.items: list[Any] = [] - self.i = 0 - - def __iter__(self): - return self - - def __next__(self): - self.i += 1 - if self.i >= len(self.items): - self.items = self.result_store.get_results(offset=self.offset, limit=self.batch_size) - self.i = 0 - self.offset += self.batch_size - if len(self.items) == 0: - raise StopIteration - return self.items[self.i] diff --git a/pysatl_experiment/experiment/report/report_step.py b/pysatl_experiment/experiment/report/report_step.py deleted file mode 100644 index 9fafbb2..0000000 --- a/pysatl_experiment/experiment/report/report_step.py +++ /dev/null @@ -1,21 +0,0 @@ -from pysatl_experiment.experiment.report.model import ResultReader -from pysatl_experiment.persistence.models import IResultStore - - -def execute_report_step(configuration, result_store: IResultStore): - data_reader = ResultReader(result_store) - # Execute before all listeners - for listener in configuration.listeners: - listener.before() - - # Process data - report_builder = configuration.report_builder - for data in data_reader: - report_builder.process(data) - - # Build report - report_builder.build() - - # Execute after all listeners - for listener in configuration.listeners: - listener.after() diff --git a/pysatl_experiment/experiment_new/step/execution/common/execution_step_data/execution_step_data.py b/pysatl_experiment/experiment/step/execution/common/execution_step_data/execution_step_data.py similarity index 100% rename from pysatl_experiment/experiment_new/step/execution/common/execution_step_data/execution_step_data.py rename to pysatl_experiment/experiment/step/execution/common/execution_step_data/execution_step_data.py diff --git a/pysatl_experiment/experiment_new/step/execution/common/hypothesis_generator_data/hypothesis_generator_data.py b/pysatl_experiment/experiment/step/execution/common/hypothesis_generator_data/hypothesis_generator_data.py similarity index 100% rename from pysatl_experiment/experiment_new/step/execution/common/hypothesis_generator_data/hypothesis_generator_data.py rename to pysatl_experiment/experiment/step/execution/common/hypothesis_generator_data/hypothesis_generator_data.py diff --git a/pysatl_experiment/experiment_new/step/execution/common/utils/utils.py b/pysatl_experiment/experiment/step/execution/common/utils/utils.py similarity index 100% rename from pysatl_experiment/experiment_new/step/execution/common/utils/utils.py rename to pysatl_experiment/experiment/step/execution/common/utils/utils.py diff --git a/pysatl_experiment/experiment_new/step/execution/critical_value/critical_value.py b/pysatl_experiment/experiment/step/execution/critical_value/critical_value.py similarity index 100% rename from pysatl_experiment/experiment_new/step/execution/critical_value/critical_value.py rename to pysatl_experiment/experiment/step/execution/critical_value/critical_value.py diff --git a/pysatl_experiment/experiment_new/step/execution/power/power.py b/pysatl_experiment/experiment/step/execution/power/power.py similarity index 100% rename from pysatl_experiment/experiment_new/step/execution/power/power.py rename to pysatl_experiment/experiment/step/execution/power/power.py diff --git a/pysatl_experiment/experiment_new/step/execution/time_complexity/time_complexity.py b/pysatl_experiment/experiment/step/execution/time_complexity/time_complexity.py similarity index 100% rename from pysatl_experiment/experiment_new/step/execution/time_complexity/time_complexity.py rename to pysatl_experiment/experiment/step/execution/time_complexity/time_complexity.py diff --git a/pysatl_experiment/experiment_new/step/generation/generation.py b/pysatl_experiment/experiment/step/generation/generation.py similarity index 100% rename from pysatl_experiment/experiment_new/step/generation/generation.py rename to pysatl_experiment/experiment/step/generation/generation.py diff --git a/pysatl_experiment/experiment_new/step/report_building/critical_value/critical_value.py b/pysatl_experiment/experiment/step/report_building/critical_value/critical_value.py similarity index 100% rename from pysatl_experiment/experiment_new/step/report_building/critical_value/critical_value.py rename to pysatl_experiment/experiment/step/report_building/critical_value/critical_value.py diff --git a/pysatl_experiment/experiment_new/step/report_building/power/power.py b/pysatl_experiment/experiment/step/report_building/power/power.py similarity index 100% rename from pysatl_experiment/experiment_new/step/report_building/power/power.py rename to pysatl_experiment/experiment/step/report_building/power/power.py diff --git a/pysatl_experiment/experiment_new/step/report_building/time_complexity/time_complexity.py b/pysatl_experiment/experiment/step/report_building/time_complexity/time_complexity.py similarity index 100% rename from pysatl_experiment/experiment_new/step/report_building/time_complexity/time_complexity.py rename to pysatl_experiment/experiment/step/report_building/time_complexity/time_complexity.py diff --git a/pysatl_experiment/experiment/test/__init__.py b/pysatl_experiment/experiment/test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pysatl_experiment/experiment/test/critical_value.py b/pysatl_experiment/experiment/test/critical_value.py deleted file mode 100644 index 444f4d9..0000000 --- a/pysatl_experiment/experiment/test/critical_value.py +++ /dev/null @@ -1,93 +0,0 @@ -import numpy as np -import scipy.stats as scipy_stats - -from pysatl_criterion.statistics import AbstractStatistic -from pysatl_experiment.experiment.hypothesis import AbstractHypothesis -from pysatl_experiment.persistence.models import ICriticalValueStore - - -def calculate_critical_value( - test: AbstractStatistic, - hypothesis: AbstractHypothesis, - size: int, - alpha: float, - count, -) -> tuple[float, list[float]]: - # Calculate critical value - distribution = np.zeros(count) - - for i in range(count): - x = hypothesis.generate(size=size) - distribution[i] = test.execute_statistic(x) - - distribution.sort() - - ecdf = scipy_stats.ecdf(distribution) - critical_value = float(np.quantile(ecdf.cdf.quantiles, q=1 - alpha)) - return critical_value, distribution.tolist() - - -def calculate_two_tailed_critical_value( - test: AbstractStatistic, - hypothesis: AbstractHypothesis, - size: int, - alpha: float, - count: int, -) -> tuple[tuple[float, float], list[float]]: - distribution = np.zeros(count) - - for i in range(count): - x = hypothesis.generate(size=size) - distribution[i] = test.execute_statistic(x) - - distribution.sort() - - ecdf = scipy_stats.ecdf(distribution) - lower_critical = float(np.quantile(ecdf.cdf.quantiles, q=alpha / 2)) - upper_critical = float(np.quantile(ecdf.cdf.quantiles, q=1 - alpha / 2)) - - return (lower_critical, upper_critical), distribution.tolist() - - -def get_or_calculate_critical_value( - test: AbstractStatistic, - hypothesis: AbstractHypothesis, - size: int, - alpha: float, - store: ICriticalValueStore, - count: int, -) -> float | tuple[float, float]: - critical_values_from_common_criterion = test.calculate_two_tailed_critical_values(size, alpha) - if critical_values_from_common_criterion is not None: - return critical_values_from_common_criterion - - critical_value_from_common_criterion = test.calculate_critical_value(size, alpha) - if critical_value_from_common_criterion is not None: - return critical_value_from_common_criterion - - critical_value = store.get_critical_value(test.code(), size, alpha) - if critical_value is not None: - return critical_value - - distribution = store.get_distribution(test.code(), size) - if distribution is not None: - ecdf = scipy_stats.ecdf(distribution) - if test.two_tailed: - lower_critical = float(np.quantile(ecdf.cdf.quantiles, q=alpha / 2)) - upper_critical = float(np.quantile(ecdf.cdf.quantiles, q=1 - alpha / 2)) - critical_value = (lower_critical, upper_critical) - else: - critical_value = float(np.quantile(ecdf.cdf.quantiles, q=1 - alpha)) - - store.insert_critical_value(test.code(), size, alpha, critical_value) - return critical_value - - if test.two_tailed: - critical_value, distribution = calculate_two_tailed_critical_value(test, hypothesis, size, alpha, count) - else: - critical_value, distribution = calculate_critical_value(test, hypothesis, size, alpha, count) - - store.insert_critical_value(test.code(), size, alpha, critical_value) - store.insert_distribution(test.code(), size, distribution) - - return critical_value diff --git a/pysatl_experiment/experiment/test/power_calculation.py b/pysatl_experiment/experiment/test/power_calculation.py deleted file mode 100644 index e4d6363..0000000 --- a/pysatl_experiment/experiment/test/power_calculation.py +++ /dev/null @@ -1,52 +0,0 @@ -from pysatl_criterion.statistics import AbstractStatistic -from pysatl_experiment.experiment.hypothesis import AbstractHypothesis -from pysatl_experiment.experiment.test.critical_value import get_or_calculate_critical_value -from pysatl_experiment.persistence.models import ICriticalValueStore - - -def execute_test( - test: AbstractStatistic, - hypothesis: AbstractHypothesis, - rvs: list[float], - alpha: float, - store: ICriticalValueStore, - count: int, -): - critical_values = get_or_calculate_critical_value(test, hypothesis, len(rvs), alpha, store, count) - - statistic = test.execute_statistic(rvs) - - if isinstance(critical_values, tuple): - lower_critical, upper_critical = critical_values - return not (lower_critical > statistic or statistic > upper_critical) - else: - return not (statistic > critical_values) - - -def calculate_test_power( - test: AbstractStatistic, - data: list[list[float]], - hypothesis: AbstractHypothesis, - alpha: float, - store: ICriticalValueStore, - count: int, -): - """ - Calculate statistic test power. - - :param hypothesis: hypothesis to test - :param store: critical value store - :param test: statistic test - :param data: alternative data - :param alpha: significant level - :param count: count of test execution - :return: - """ - - k = 0 - for rvs in data: - x = execute_test(test, hypothesis, rvs, alpha, store, count) - if x is False: - k = k + 1 - - return k / len(data) diff --git a/pysatl_experiment/experiment/test/test_step.py b/pysatl_experiment/experiment/test/test_step.py deleted file mode 100644 index 6ea4a64..0000000 --- a/pysatl_experiment/experiment/test/test_step.py +++ /dev/null @@ -1,124 +0,0 @@ -import logging -from collections.abc import Sequence -from multiprocessing import Queue -from multiprocessing.synchronize import Event as EventClass - -from tqdm import tqdm - -from pysatl_criterion.statistics import AbstractStatistic -from pysatl_experiment.experiment.configuration.configuration import TestConfiguration, TestWorker -from pysatl_experiment.experiment.pipeline import start_pipeline -from pysatl_experiment.persistence import IRvsStore -from pysatl_experiment.persistence.models import IResultStore - - -logger = logging.getLogger(__name__) - - -def execute_tests( - worker: TestWorker, - tests: list[AbstractStatistic], - rvs_store: IRvsStore, - result_store: IResultStore, - thread_count: int = 0, -): - rvs_store.init() - result_store.init() - worker.init() - - stat = rvs_store.get_rvs_stat() - text = f"#{thread_count}" - pbar = tqdm(total=len(stat) * len(tests), desc=text, position=thread_count) - for code, size, _ in stat: - data = rvs_store.get_rvs(code, size) - for test in tests: - result_id = worker.build_id(test, data, code, size) - result = result_store.get_result(result_id) - if result is None: - result = worker.execute(test, data, code, size) - result_store.insert_result(result_id, result) - pbar.update(1) - - -def process_entries( - generate_queue: Queue, - info_queue: Queue, - generate_shutdown_event: EventClass, - info_shutdown_event: EventClass, - kwargs, -): - worker: TestWorker = kwargs["worker"] - result_store: IResultStore = kwargs["result_store"] - worker.init() - - while not (generate_shutdown_event.is_set() and generate_queue.empty()): - if not generate_queue.empty(): - test, data, code, size = generate_queue.get() - result_id = worker.build_id(test, data, code, size) - result = result_store.get_result(result_id) - - if result is None: - result = worker.execute(test, data, code, size) - result_store.insert_result(result_id, result) - - info_queue.put(1) - - info_shutdown_event.set() - - -def fill_queue(queue, generate_shutdown_event, kwargs): - tests: list[AbstractStatistic] = kwargs["tests"] - store: IRvsStore = kwargs["store"] - - store.init() - stat = store.get_rvs_stat() - - for code, size, _ in stat: - data = store.get_rvs(code, size) - for test in tests: - queue.put((test, data, code, size)) - - generate_shutdown_event.set() - - return len(stat) * len(tests) - - -def get_total_count(tests: Sequence[AbstractStatistic], store: IRvsStore): - store.init() - stat = store.get_rvs_stat() - return len(stat) * len(tests) - - -def execute_test_step(configuration: TestConfiguration, rvs_store: IRvsStore, result_store: IResultStore): - threads_count = configuration.threads - worker = configuration.worker - tests = configuration.tests - - # Skip step - if configuration.skip_step: - logger.info("Skip test step") - return - - logger.info("Start test step") - - # Execute before all listeners - for listener in configuration.listeners: - listener.before() - - start_pipeline( - fill_queue, - process_entries, - threads_count, - total_count=get_total_count(tests, rvs_store), - queue_size=10, - worker=worker, - tests=tests, - store=rvs_store, - result_store=result_store, - ) - - # Execute after all listeners - for listener in configuration.listeners: - listener.after() - - logger.info("End test step") diff --git a/pysatl_experiment/experiment/test/worker.py b/pysatl_experiment/experiment/test/worker.py deleted file mode 100644 index c1c9484..0000000 --- a/pysatl_experiment/experiment/test/worker.py +++ /dev/null @@ -1,49 +0,0 @@ -from typing_extensions import override - -from pysatl_criterion.statistics import AbstractStatistic -from pysatl_experiment.experiment.configuration.configuration import TestWorker, TestWorkerResult -from pysatl_experiment.experiment.hypothesis import AbstractHypothesis -from pysatl_experiment.experiment.test.power_calculation import calculate_test_power -from pysatl_experiment.persistence.models import ICriticalValueStore - - -class PowerWorkerResult(TestWorkerResult): - def __init__(self, test_code, alternative_code, size, alpha, power): - self.test_code = test_code - self.alpha = alpha - self.size = size - self.power = power - self.alternative_code = alternative_code - - -class BenchmarkWorkerResult(TestWorkerResult): - def __init__(self, size: int, test_code: str, benchmark: list[float]): - self.size = size - self.benchmark = benchmark - self.test_code = test_code - - -class PowerCalculationWorker(TestWorker): - def __init__( - self, - alpha: float, - monte_carlo_count: int, - cv_store: ICriticalValueStore, - hypothesis: AbstractHypothesis, - ): - self.alpha = alpha - self.monte_carlo_count = monte_carlo_count - self.cv_store = cv_store - self.hypothesis = hypothesis - - @override - def init(self): - self.cv_store.init() - - def build_id(self, test: AbstractStatistic, data: list[list[float]], code: str, size: int) -> str: - return "_".join([str(self.alpha), str(size), test.code(), code]) - - @override - def execute(self, test: AbstractStatistic, data: list[list[float]], code: str, size: int) -> PowerWorkerResult: - power = calculate_test_power(test, data, self.hypothesis, self.alpha, self.cv_store, self.monte_carlo_count) - return PowerWorkerResult(test.code(), code, size, self.alpha, power) diff --git a/pysatl_experiment/experiment_new/__init__.py b/pysatl_experiment/experiment_new/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pysatl_experiment/persistence/__init__.py b/pysatl_experiment/persistence/__init__.py index bfd4adf..e69de29 100644 --- a/pysatl_experiment/persistence/__init__.py +++ b/pysatl_experiment/persistence/__init__.py @@ -1,4 +0,0 @@ -from pysatl_experiment.persistence.models import ICriticalValueStore, IRvsStore, IStore - - -__all__ = ["ICriticalValueStore", "IRvsStore", "IStore"] diff --git a/pysatl_experiment/persistence/db_store/__init__.py b/pysatl_experiment/persistence/db_store/__init__.py deleted file mode 100644 index 99620c6..0000000 --- a/pysatl_experiment/persistence/db_store/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -from pysatl_experiment.persistence.db_store.base import ModelBase, SessionType -from pysatl_experiment.persistence.db_store.critical_value_store import CriticalValueDbStore -from pysatl_experiment.persistence.db_store.db_init import get_request_or_thread_id, init_db -from pysatl_experiment.persistence.db_store.result_store import ResultDbStore -from pysatl_experiment.persistence.db_store.rvs_store import RvsDbStore - - -__all__ = [ - "RvsDbStore", - "get_request_or_thread_id", - "init_db", - "CriticalValueDbStore", - "ModelBase", - "SessionType", - "ResultDbStore", -] diff --git a/pysatl_experiment/persistence/db_store/base.py b/pysatl_experiment/persistence/db_store/base.py deleted file mode 100644 index 5f5c40d..0000000 --- a/pysatl_experiment/persistence/db_store/base.py +++ /dev/null @@ -1,8 +0,0 @@ -from sqlalchemy.orm import DeclarativeBase, Session, scoped_session - - -SessionType = scoped_session[Session] - - -class ModelBase(DeclarativeBase): - pass diff --git a/pysatl_experiment/persistence/db_store/critical_value_store.py b/pysatl_experiment/persistence/db_store/critical_value_store.py deleted file mode 100644 index 8f60f3b..0000000 --- a/pysatl_experiment/persistence/db_store/critical_value_store.py +++ /dev/null @@ -1,91 +0,0 @@ -from typing import ClassVar - -from sqlalchemy import Float, Integer, String -from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import Mapped, mapped_column -from typing_extensions import override - -from pysatl_experiment.persistence.db_store.base import ModelBase, SessionType -from pysatl_experiment.persistence.db_store.model import AbstractDbStore -from pysatl_experiment.persistence.models import ICriticalValueStore - - -class Distribution(ModelBase): - """ - Distribution data database model. - - """ - - __tablename__ = "distribution" - code: Mapped[str] = mapped_column(String(50), primary_key=True) # type: ignore - size: Mapped[int] = mapped_column(Integer, primary_key=True) # type: ignore - data: Mapped[str] = mapped_column(String(), nullable=False) # type: ignore - - -class CriticalValue(ModelBase): - """ - Critical value data database model. - - """ - - __tablename__ = "critical_value" - - code: Mapped[str] = mapped_column(String(50), primary_key=True) # type: ignore - size: Mapped[int] = mapped_column(Integer, primary_key=True) # type: ignore - sl: Mapped[float] = mapped_column(Float, primary_key=True) # type: ignore - lower_value: Mapped[float] = mapped_column(Float, nullable=True) - upper_value: Mapped[float] = mapped_column(Float, nullable=True) - - -class CriticalValueDbStore(AbstractDbStore, ICriticalValueStore): - session: ClassVar[SessionType] - __separator = ";" - - @override - def insert_critical_value(self, code: str, size: int, sl: float, value: float | tuple[float, float]): - if isinstance(value, tuple): - lower_value, upper_value = value - else: - lower_value, upper_value = value, None - - try: - CriticalValueDbStore.session.add( - CriticalValue( - code=code, - size=int(size), - sl=sl, - lower_value=lower_value, - upper_value=upper_value, - ) - ) - CriticalValueDbStore.session.commit() - except IntegrityError: - CriticalValueDbStore.session.rollback() - - @override - def insert_distribution(self, code: str, size: int, data: list[float]): - data_to_insert = CriticalValueDbStore.__separator.join(map(str, data)) - try: - CriticalValueDbStore.session.add(Distribution(code=code, size=int(size), data=data_to_insert)) - CriticalValueDbStore.session.commit() - except IntegrityError: - CriticalValueDbStore.session.rollback() - - @override - def get_critical_value(self, code: str, size: int, sl: float) -> float | tuple[float, float] | None: - critical_value = CriticalValueDbStore.session.get(CriticalValue, (code, size, sl)) - if critical_value is not None: - if critical_value.upper_value is not None: - return critical_value.lower_value, critical_value.upper_value - else: - return critical_value.lower_value - else: - return None - - @override - def get_distribution(self, code: str, size: int) -> list[float] | None: - distribution = CriticalValueDbStore.session.get(Distribution, (code, size)) - if distribution is not None: - return [float(x) for x in distribution.data.split(CriticalValueDbStore.__separator)] - else: - return None diff --git a/pysatl_experiment/persistence/db_store/db_init.py b/pysatl_experiment/persistence/db_store/db_init.py deleted file mode 100644 index b78fd30..0000000 --- a/pysatl_experiment/persistence/db_store/db_init.py +++ /dev/null @@ -1,70 +0,0 @@ -""" -This module contains the class to persist trades into SQLite -""" - -import logging -import threading -from contextvars import ContextVar -from typing import Any, Final - -from sqlalchemy import Engine, create_engine -from sqlalchemy.exc import NoSuchModuleError -from sqlalchemy.pool import StaticPool - -from pysatl_experiment.exceptions import OperationalException - - -logger = logging.getLogger(__name__) - -REQUEST_ID_CTX_KEY: Final[str] = "request_id" -_request_id_ctx_var: ContextVar[str | None] = ContextVar(REQUEST_ID_CTX_KEY, default=None) - - -def get_request_or_thread_id() -> str | None: - """ - Helper method to get either async context, or thread id - """ - request_id = _request_id_ctx_var.get() - if request_id is None: - # when not in request context - use thread id - request_id = str(threading.current_thread().ident) - - return request_id - - -_SQL_DOCS_URL = "http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls" - - -def init_db(db_url: str) -> Engine: - """ - Initializes this module with the given config, - registers all known command handlers - and starts polling for message updates - :param db_url: Database to use - :return: None - """ - kwargs: dict[str, Any] = {} - - if db_url == "sqlite:///": - raise OperationalException(f"Bad db-url {db_url}. For in-memory database, please use `sqlite://`.") - if db_url == "sqlite://": - kwargs.update( - { - "poolclass": StaticPool, - } - ) - # Take care of thread ownership - if db_url.startswith("sqlite://"): - kwargs.update( - { - "connect_args": {"check_same_thread": False}, - } - ) - - try: - engine = create_engine(db_url, future=True, **kwargs) - except NoSuchModuleError: - raise OperationalException( - f"Given value for db_url: '{db_url}' is no valid database URL! (See {_SQL_DOCS_URL})" - ) - return engine diff --git a/pysatl_experiment/persistence/db_store/model.py b/pysatl_experiment/persistence/db_store/model.py deleted file mode 100644 index 2ae995c..0000000 --- a/pysatl_experiment/persistence/db_store/model.py +++ /dev/null @@ -1,28 +0,0 @@ -import sqlite3 -from abc import ABC -from typing import ClassVar - -import numpy as np -from sqlalchemy.orm import scoped_session, sessionmaker -from typing_extensions import override - -from pysatl_experiment.persistence import IStore -from pysatl_experiment.persistence.db_store.base import ModelBase, SessionType -from pysatl_experiment.persistence.db_store.db_init import get_request_or_thread_id, init_db - - -class AbstractDbStore(IStore, ABC): - session: ClassVar[SessionType] - - def __init__(self, db_url="sqlite:///pysatl.sqlite"): - super().__init__() - self.db_url = db_url - - @override - def init(self): - sqlite3.register_adapter(np.int64, lambda val: int(val)) - engine = init_db(self.db_url) - AbstractDbStore.session = scoped_session( - sessionmaker(bind=engine, autoflush=False), scopefunc=get_request_or_thread_id - ) - ModelBase.metadata.create_all(engine) diff --git a/pysatl_experiment/persistence/db_store/result_store.py b/pysatl_experiment/persistence/db_store/result_store.py deleted file mode 100644 index 2cd4de6..0000000 --- a/pysatl_experiment/persistence/db_store/result_store.py +++ /dev/null @@ -1,78 +0,0 @@ -import importlib -import json -from typing import Any, ClassVar - -from sqlalchemy import String -from sqlalchemy.orm import Mapped, mapped_column -from typing_extensions import override - -from pysatl_experiment.persistence.db_store import ModelBase, SessionType -from pysatl_experiment.persistence.db_store.model import AbstractDbStore -from pysatl_experiment.persistence.models import IResultStore - - -class ResultModel(ModelBase): - """ - Result database model. - """ - - __tablename__ = "result" - - id: Mapped[str] = mapped_column(String, primary_key=True) - module: Mapped[str] = mapped_column(String) - className: Mapped[str] = mapped_column(String) - data: Mapped[str] = mapped_column(String, nullable=False) - - -class ResultDbStore(AbstractDbStore, IResultStore): - session: ClassVar[SessionType] - __separator = ";" - - @override - def insert_result(self, result_id: str, result: Any): - """ - Insert benchmark to store. - - :param test_code: test code - :param benchmark: benchmark - """ - - json_data = json.dumps(result.__dict__) - data = ResultModel( - id=result_id, - module=result.__module__, - className=result.__class__.__name__, - data=json_data, - ) - ResultDbStore.session.add(data) - ResultDbStore.session.commit() - - @override - def get_result(self, result_id: str) -> Any: - """ - Get benchmark from store. - - :param result_id: test code - - :return benchmark on None - """ - result = ResultDbStore.session.get(ResultModel, result_id) - if not result: - return None - - module = importlib.import_module(result.module) - return getattr(module, result.className)(**json.loads(result.data)) - - @override - def get_results(self, offset: int, limit: int): # -> [PowerResultModel]: - """ - Get several powers from store. - - :param offset: offset - :param limit: limit - - :return list of PowerResultModel - """ - result = (ResultDbStore.session.query(ResultModel).order_by(ResultModel.id).offset(offset).limit(limit)).all() - result = [getattr(importlib.import_module(r.module), r.className)(**json.loads(r.data)) for r in result] - return result diff --git a/pysatl_experiment/persistence/db_store/rvs_store.py b/pysatl_experiment/persistence/db_store/rvs_store.py deleted file mode 100644 index 4b12712..0000000 --- a/pysatl_experiment/persistence/db_store/rvs_store.py +++ /dev/null @@ -1,97 +0,0 @@ -from typing import ClassVar - -from sqlalchemy import Integer, Row, String, func, text -from sqlalchemy.orm import Mapped, mapped_column -from typing_extensions import override - -from pysatl_experiment.persistence import IRvsStore -from pysatl_experiment.persistence.db_store.base import ModelBase, SessionType -from pysatl_experiment.persistence.db_store.model import AbstractDbStore - - -class RVS(ModelBase): - """ - RVS data database model. - - """ - - __tablename__ = "rvs_data" - - id: Mapped[int] = mapped_column(Integer, primary_key=True) # type: ignore - code: Mapped[str] = mapped_column(String(50), nullable=False, index=True) # type: ignore - size: Mapped[int] = mapped_column(Integer, nullable=False, index=True) # type: ignore - data: Mapped[str] = mapped_column(String(), nullable=False) # type: ignore - - -class RVSStat(ModelBase): - """ - RVS stat data database model. - - """ - - __tablename__ = "rvs_stat" - - code: Mapped[str] = mapped_column(String(50), primary_key=True) # type: ignore - size: Mapped[int] = mapped_column(Integer, primary_key=True) # type: ignore - count: Mapped[int] = mapped_column(Integer) # type: ignore - - -class RvsDbStore(AbstractDbStore, IRvsStore): - session: ClassVar[SessionType] - __separator = ";" - - @override - def insert_all_rvs(self, generator_code: str, size: int, data: list[list[float]]): - if len(data) == 0: - return - - data_to_insert = [ - { - "code": generator_code, - "size": int(size), - "data": RvsDbStore.__separator.join(map(str, d)), - } - for d in data - ] - statement = text("INSERT INTO rvs_data (code, size, data) VALUES (:code, :size, :data)") - RvsDbStore.session.execute(statement, data_to_insert) - RvsDbStore.session.commit() - - @override - def insert_rvs(self, code: str, size: int, data: list[float]): - data_str = RvsDbStore.__separator.join(map(str, data)) - RvsDbStore.session.add(RVS(code=code, size=int(size), data=data_str)) - RvsDbStore.session.commit() - - @override - def get_rvs_count(self, code: str, size: int): - data = self.get_rvs(code, size) - return len(data) - - @override - def get_rvs(self, code: str, size: int) -> list[list[float]]: - samples = ( - RvsDbStore.session.query(RVS) - .filter( - RVS.code == code, - RVS.size == size, - ) - .all() - ) - - if not samples: - return [] - - return [[float(x) for x in sample.data.split(RvsDbStore.__separator)] for sample in samples] - - @override - def get_rvs_stat(self) -> list[tuple[str, int, int]]: - query_result: list[Row[tuple[str, int, int]]] = ( - RvsDbStore.session.query(RVS.code, RVS.size, func.count(RVS.code)).group_by(RVS.code, RVS.size).all() - ) - - return [tuple(row) for row in query_result] - - @override - def clear_all_rvs(self): - RvsDbStore.session.query(RVS).delete() diff --git a/pysatl_experiment/persistence/file_store/__init__.py b/pysatl_experiment/persistence/file_store/__init__.py deleted file mode 100644 index 97225da..0000000 --- a/pysatl_experiment/persistence/file_store/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from pysatl_experiment.persistence.file_store.critical_value_store import CriticalValueFileStore -from pysatl_experiment.persistence.file_store.rvs_store import RvsFileStore - - -__all__ = ["CriticalValueFileStore", "RvsFileStore"] diff --git a/pysatl_experiment/persistence/file_store/critical_value_store.py b/pysatl_experiment/persistence/file_store/critical_value_store.py deleted file mode 100644 index 32b4c1f..0000000 --- a/pysatl_experiment/persistence/file_store/critical_value_store.py +++ /dev/null @@ -1,136 +0,0 @@ -import csv -from pathlib import Path - -from typing_extensions import override - -from pysatl_experiment.persistence import ICriticalValueStore -from pysatl_experiment.persistence.file_store.store import read_json, write_json - - -class CriticalValueFileStore(ICriticalValueStore): - csv_delimiter = ";" - separator = ":" - - def __init__(self, path="test_distribution"): - super().__init__() - self.path = path - self.filename = Path(path, "critical_value.json") - self.cache = {} - - @override - def init(self): - if not Path(self.path).exists(): - Path(self.path).mkdir(parents=True) - mem_cache = {} - if Path(self.filename).is_file(): - mem_cache = read_json(self.filename) - self.cache = mem_cache - - @override - def insert_critical_value(self, code: str, size: int, sl: float, value: float | tuple[float, float]): - """ - Insert critical value to store. - - :param code: test code - :param size: rvs size - :param sl: significant level - :param value: critical value - """ - - key = self._create_key([code, str(size), str(sl)]) - self.cache[key] = value - write_json(self.filename, self.cache) - - def insert_distribution(self, code: str, size: int, data: list[float]): - """ - Save distribution to csv file. Name generated by {test_code}_{size}.scv - - :param code: statistic test code - :param size: sample size - :param data: distribution data to save - """ - - file_path = self.__build_file_path(code, size) - with Path(file_path).open("w", newline="") as csvfile: - writer = csv.writer(csvfile, delimiter=self.csv_delimiter, quoting=csv.QUOTE_NONNUMERIC) - writer.writerow(data) - - def get_critical_value(self, code: str, size: int, sl: float) -> tuple[float, float] | float | None: - """ - Get critical value from store. - :param code: test code - :param size: rvs size - :param sl: significant level - """ - - key = self._create_key([code, str(size), str(sl)]) - if key not in self.cache.keys(): - return None - - return self.cache[key] - - def get_distribution(self, code: str, size: int) -> list[float] | None: - """ - Return distribution cached value or None. - - :param code: statistic test code - :param size: sample size - """ - - file_path = self.__build_file_path(code, size) - if Path(file_path).exists(): - with Path(file_path).open(newline="") as f: - reader = csv.reader(f, delimiter=self.csv_delimiter, quoting=csv.QUOTE_NONNUMERIC) - return [float(x) for x in list(reader)[0]] - else: - return None - - def __build_file_path(self, test_code: str, size: int): - file_name = test_code + "_" + str(size) + ".csv" - return Path(self.path, file_name) - - def _create_key(self, keys: list[str]): - return self.separator.join(keys) - - -class ThreadSafeMonteCarloCacheService(CriticalValueFileStore): - def __init__( - self, - lock, - filename="cache.json", - separator=":", - csv_delimiter=";", - dir_path="test_distribution", - ): - super().__init__(filename, separator, csv_delimiter, dir_path) - self.lock = lock - - def flush(self): - """ - Flush data to persisted store. - """ - - with self.lock: - write_json(self.filename, self.cache) - - def put(self, key: str, value): - """ - Put object to cache. - - :param key: cache key - :param value: cache value - """ - with self.lock: - self.cache[key] = value - - def put_with_level(self, keys: list[str], value): - """ - Put JSON value by keys chain in 'keys' param. - - :param value: value to put - :param keys: keys chain param - """ - - key = self._create_key(keys) - with self.lock: - self.cache[key] = value diff --git a/pysatl_experiment/persistence/file_store/rvs_store.py b/pysatl_experiment/persistence/file_store/rvs_store.py deleted file mode 100644 index c7ec9aa..0000000 --- a/pysatl_experiment/persistence/file_store/rvs_store.py +++ /dev/null @@ -1,87 +0,0 @@ -import csv -import os -import shutil -from pathlib import Path - -from typing_extensions import override - -from pysatl_experiment.persistence import IRvsStore - - -class RvsFileStore(IRvsStore): - __separator = ";" - __file_separator = "_" - - def __init__(self, path="data"): - super().__init__() - self.path = path - - @override - def init(self): - if not Path(self.path).exists(): - Path(self.path).mkdir(parents=True) - - @override - def insert_all_rvs(self, generator_code: str, size: int, data: list[list[float]]): - file_path = Path(self.path, RvsFileStore.build_rvs_file_name(generator_code, size) + ".csv") - with Path(file_path).open("w", newline="") as csvfile: - writer = csv.writer( - csvfile, - delimiter=RvsFileStore.__separator, - quoting=csv.QUOTE_NONNUMERIC, - ) - writer.writerows(data) - - @override - def get_rvs_stat(self): - filenames = next(os.walk(self.path), (None, None, []))[2] # [] if no file - result = [] - for filename in filenames: - rvs_code, size = RvsFileStore.parse_rvs_file_name(filename) - file_path = Path(self.path, RvsFileStore.build_rvs_file_name(rvs_code, size) + ".csv") - with Path(file_path).open(newline="") as f: - reader = csv.reader(f, delimiter=RvsFileStore.__separator, quoting=csv.QUOTE_NONNUMERIC) - result.append((rvs_code, size, len(list(reader)))) - - return result - - @override - def insert_rvs(self, code: str, size: int, data: list[float]): - file_path = Path(self.path, RvsFileStore.build_rvs_file_name(code, size) + ".csv") - with Path(file_path).open("w", newline="") as csvfile: - writer = csv.writer( - csvfile, - delimiter=RvsFileStore.__separator, - quoting=csv.QUOTE_NONNUMERIC, - ) - writer.writerow(data) - - @override - def get_rvs_count(self, code: str, size: int) -> int: - data = self.get_rvs(code, size) - if data is None: - return 0 - return len(data) - - @override - def get_rvs(self, code: str, size: int) -> list[list[float]]: - file_path = Path(self.path, RvsFileStore.build_rvs_file_name(code, size) + ".csv") - if not Path(file_path).exists(): - return [] - with Path(file_path).open(newline="") as f: - reader = csv.reader(f, delimiter=RvsFileStore.__separator, quoting=csv.QUOTE_NONNUMERIC) - return [[float(x) for x in e] for e in reader] - - @override - def clear_all_rvs(self): - shutil.rmtree(self.path) - - @staticmethod - def parse_rvs_file_name(name: str) -> tuple: - split = name.split(RvsFileStore.__file_separator) - size = int(split[-2]) - return RvsFileStore.__file_separator.join(split[:-2]), size - - @staticmethod - def build_rvs_file_name(generator_code: str, size: int) -> str: - return generator_code + RvsFileStore.__file_separator + str(size) + RvsFileStore.__file_separator diff --git a/pysatl_experiment/persistence/file_store/store.py b/pysatl_experiment/persistence/file_store/store.py deleted file mode 100644 index 0fd7de2..0000000 --- a/pysatl_experiment/persistence/file_store/store.py +++ /dev/null @@ -1,144 +0,0 @@ -import json -from pathlib import Path - - -def read_json(filename: str): - with Path(filename).open("w") as f_in: - return json.load(f_in) - - -def write_json(filename: str, value): - with Path(filename).open("w") as fp: - json.dump(value, fp) - - -class StoreService: - def get(self, key: str): - """ - Get cached value if exists, else return None. - - :param key: cache_services key - """ - raise NotImplementedError("Method is not implemented") - - def put(self, key: str, value): - """ - Put object to cache_services. - - :param key: cache_services key - :param value: cache_services value - """ - raise NotImplementedError("Method is not implemented") - - -class InMemoryStoreService(StoreService): - def __init__(self, cache=None, separator="."): - if cache is None: - cache = {} - self.cache = cache - self.separator = separator - - def get(self, key: str): - """ - Get cached value if exists, else return None. - - :param key: cache_services key - """ - - if key not in self.cache.keys(): - return None - - return self.cache[key] - - def get_with_level(self, keys: list[str]): - """ - Get JSON value by keys chain in 'keys' param. - - :param keys: keys chain param - """ - - key = self._create_key(keys) - return self.get(key) - - def put(self, key: str, value): - """ - Put object to cache_services. - - :param key: cache_services key - :param value: cache_services value - """ - - self.cache[key] = value - - def put_with_level(self, keys: list[str], value): - """ - Put JSON value by keys chain in 'keys' param. - - :param value: value to put - :param keys: keys chain param - """ - - key = self._create_key(keys) - self.put(key, value) - - def _create_key(self, keys: list[str]): - return self.separator.join(keys) - - -class JsonStoreService(InMemoryStoreService): - def __init__(self, filename="cache.json", separator="."): - super().__init__(separator=separator) - mem_cache = {} - if Path(filename).is_file(): - mem_cache = read_json(filename) - self.cache = mem_cache - self.filename = filename - self.separator = separator - - def put(self, key: str, value): - """ - Put object to cache_services. - - :param key: cache_services key - :param value: cache_services value - """ - super().put(key, value) - write_json(self.filename, self.cache) - - def put_with_level(self, keys: list[str], value): - """ - Put JSON value by keys chain in 'keys' param. - - :param value: value to put - :param keys: keys chain param - """ - - super().put_with_level(keys, value) - write_json(self.filename, self.cache) - - -class FastStoreService(InMemoryStoreService): - def flush(self): - """ - Flush data to persisted store. - """ - - raise NotImplementedError("Method is not implemented") - - -class FastJsonStoreService(FastStoreService): - def __init__(self, filename="cache.json", separator="."): - super().__init__(separator=separator) - mem_cache = {} - if Path(filename).is_file(): - mem_cache = read_json(filename) - self.cache = mem_cache - self.filename = filename - self.separator = separator - - def flush(self): - """ - Flush data to persisted store. - """ - - write_json(self.filename, self.cache) diff --git a/pysatl_experiment/persistence/models.py b/pysatl_experiment/persistence/models.py deleted file mode 100644 index 33da85c..0000000 --- a/pysatl_experiment/persistence/models.py +++ /dev/null @@ -1,161 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Any - - -class IStore: - def migrate(self): - """ - Migrate store. - """ - pass - - def init(self): - """ - Initialize store. - """ - pass - - -class IRvsStore(IStore, ABC): - def insert_all_rvs(self, generator_code: str, size: int, data: list[list[float]]): - """ - Insert several rvs data to store. - By default use single rvs insert. - - :param generator_code: generator unique code - :param size: rvs size - :param data: list of lists rvs data - """ - for d in data: - self.insert_rvs(generator_code, size, d) - - @abstractmethod - def insert_rvs(self, generator_code: str, size: int, data: list[float]): - """ - Insert one rvs data to store. - - :param generator_code: generator unique code - :param size: rvs size - :param data: list of lists rvs data - """ - pass - - def get_rvs_count(self, generator_code: str, size: int) -> int: - """ - Get rvs count from store. - By default use get rvs data. - - :param generator_code: generator unique code - :param size: rvs size - - Return rvs data count - """ - return len(self.get_rvs(generator_code, size)) - - @abstractmethod - def get_rvs(self, generator_code: str, size: int) -> list[list[float]]: - """ - Get rvs data from store. - - :param generator_code: generator unique code - :param size: rvs size - - Return list of lists rvs data - """ - pass - - @abstractmethod - def get_rvs_stat(self) -> list[tuple[str, int, int]]: - """ - Get rvs data statistics. - - Return list of tuples (generator code, size, count) - """ - pass - - @abstractmethod - def clear_all_rvs(self): - """ - Clear ALL data in store. - """ - pass - - -class ICriticalValueStore(IStore, ABC): - @abstractmethod - def insert_critical_value(self, code: str, size: int, sl: float, value: float | tuple[float, float]): - """ - Insert critical value to store. - - :param code: test code - :param size: rvs size - :param sl: significant level - :param value: critical value - """ - pass - - @abstractmethod - def insert_distribution(self, code: str, size: int, data: list[float]): - """ - Insert distribution to store. - - :param code: test code - :param size: rvs size - :param data: distribution data - """ - pass - - @abstractmethod - def get_critical_value(self, code: str, size: int, sl: float) -> float | tuple[float, float] | None: - """ - Get critical value from store. - :param code: test code - :param size: rvs size - :param sl: significant level - """ - pass - - @abstractmethod - def get_distribution(self, code: str, size: int) -> list[float] | None: - """ - Get distribution from store. - - :param code: test code - :param size: rvs size - """ - pass - - -class IResultStore(IStore): - @abstractmethod - def insert_result(self, result_id: str, result: Any): - """ - Insert benchmark to store. - - :param result_id: result id - :param result: the result - """ - pass - - @abstractmethod - def get_result(self, result_id: str) -> Any: - """ - Get benchmark from store. - - :param result_id: result id - - :return: result or None - """ - pass - - @abstractmethod - def get_results(self, offset: int, limit: int): # -> [PowerResultModel]: - """ - Get several powers from store. - - :param offset: offset - :param limit: limit - - :return: list of results - """ - pass diff --git a/pysatl_experiment/resolvers/__init__.py b/pysatl_experiment/resolvers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pysatl_experiment/resolvers/generator_resolver.py b/pysatl_experiment/resolvers/generator_resolver.py deleted file mode 100644 index 929108f..0000000 --- a/pysatl_experiment/resolvers/generator_resolver.py +++ /dev/null @@ -1,111 +0,0 @@ -# pragma pylint: disable=attribute-defined-outside-init - -""" -This module load custom RVS generators -""" - -import logging -from typing import Any - -from pysatl_experiment.constants import USERPATH_GENERATORS, Config -from pysatl_experiment.exceptions import OperationalException -from pysatl_experiment.experiment.generator import AbstractRVSGenerator -from pysatl_experiment.resolvers.iresolver import IResolver - - -logger = logging.getLogger(__name__) - - -class GeneratorResolver(IResolver): - """ - This class contains the logic to load custom RVS generator class - """ - - object_type = AbstractRVSGenerator - object_type_str = "AbstractRVSGenerator" - user_subdir = USERPATH_GENERATORS - initial_search_path = None - extra_path = "generator_path" - module_names = ["pysatl_experiment.experiment.generator"] - - @staticmethod - def load_generators(config: Config | None) -> list[AbstractRVSGenerator]: - if not config: - raise OperationalException("No configuration set. Please specify configuration.") - - if not config.get("alternatives_configuration"): - raise OperationalException("No alternatives configuration set.") - - alternatives_configuration = config["alternatives_configuration"] - if not alternatives_configuration.get("alternatives"): - raise OperationalException("No alternatives set.") - - alternatives = alternatives_configuration["alternatives"] - generators = [] - for generator_conf in alternatives: - generator = GeneratorResolver.load_generator(generator_conf["name"], generator_conf["params"]) - generators.append(generator) - - return generators - - @staticmethod - def load_generator( - generator_name: str, path: str | None = None, params: dict[str, Any] | None = None - ) -> AbstractRVSGenerator: - """ - Load the custom class from config parameter - :param params: - :param path: - :param generator_name: - """ - - generator: AbstractRVSGenerator = GeneratorResolver._load_generator( - generator_name, params=params, extra_dir=path - ) - - return generator - - @staticmethod - def validate_generator(generator: AbstractRVSGenerator) -> AbstractRVSGenerator: - # Validation can be added - return generator - - @staticmethod - def _load_generator( - generator_name: str, - params: dict[str, Any] | None, - extra_dir: str | None = None, - ) -> AbstractRVSGenerator: - """ - Search and loads the specified strategy. - :param generator_name: name of the module to import - :param config: configuration for the strategy - :param extra_dir: additional directory to search for the given strategy - :return: Strategy instance or None - """ - extra_dirs = [] - - if extra_dir: - extra_dirs.append(extra_dir) - - abs_paths = GeneratorResolver.build_search_paths( - user_data_dir=None, user_subdir=USERPATH_GENERATORS, extra_dirs=extra_dirs - ) - - generator = GeneratorResolver._load_object( - paths=abs_paths, - object_name=generator_name, - add_source=True, - kwargs=params, - ) - - if not generator: - generator = GeneratorResolver._load_modules_object(object_name=generator_name, kwargs=params) - - if generator: - return GeneratorResolver.validate_generator(generator) - - raise OperationalException( - f"Impossible to load RVS generator '{generator_name}'. This class does not exist " - "or contains Python code errors." - ) diff --git a/pysatl_experiment/resolvers/hypothesis_resolver.py b/pysatl_experiment/resolvers/hypothesis_resolver.py deleted file mode 100644 index 44130c1..0000000 --- a/pysatl_experiment/resolvers/hypothesis_resolver.py +++ /dev/null @@ -1,93 +0,0 @@ -# pragma pylint: disable=attribute-defined-outside-init - -""" -This module load custom RVS generators -""" - -import logging -from typing import Any - -from pysatl_experiment.constants import USERPATH_GENERATORS -from pysatl_experiment.exceptions import OperationalException -from pysatl_experiment.experiment.hypothesis import AbstractHypothesis -from pysatl_experiment.resolvers.iresolver import IResolver - - -logger = logging.getLogger(__name__) - - -class HypothesisResolver(IResolver): - """ - This class contains the logic to load custom RVS generator class - """ - - object_type = AbstractHypothesis - object_type_str = "AbstractHypothesis" - user_subdir = USERPATH_GENERATORS - initial_search_path = None - extra_path = "hypothesis_path" - module_name = "pysatl_experiment.experiment.hypothesis" - - @staticmethod - def load_hypothesis( - hypothesis_name: str, path: str | None = None, params: dict[str, Any] | None = None - ) -> AbstractHypothesis: - """ - Load the custom class from config parameter - :param params: - :param path: - :param hypothesis_name: - """ - - hypothesis: AbstractHypothesis = HypothesisResolver._load_hypothesis( - hypothesis_name, params=params, extra_dir=path - ) - - return hypothesis - - @staticmethod - def validate_hypothesis(generator: AbstractHypothesis) -> AbstractHypothesis: - # Validation can be added - return generator - - @staticmethod - def _load_hypothesis( - hypothesis_name: str, - params: dict[str, Any] | None, - extra_dir: str | None = None, - ) -> AbstractHypothesis: - """ - Search and loads the specified strategy. - :param hypothesis_name: name of the module to import - :param config: configuration for the strategy - :param extra_dir: additional directory to search for the given strategy - :return: Strategy instance or None - """ - extra_dirs = [] - - if extra_dir: - extra_dirs.append(extra_dir) - - abs_paths = HypothesisResolver.build_search_paths( - user_data_dir=None, user_subdir=USERPATH_GENERATORS, extra_dirs=extra_dirs - ) - - hypothesis = HypothesisResolver._load_object( - paths=abs_paths, - object_name=hypothesis_name, - add_source=True, - kwargs=params, - ) - - if not hypothesis: - hypothesis = HypothesisResolver._load_module_object( - object_name=hypothesis_name, kwargs=params, module_name="" - ) - - if hypothesis: - return HypothesisResolver.validate_hypothesis(hypothesis) - - raise OperationalException( - f"Impossible to load RVS hypothesis '{hypothesis_name}'. This class does not exist " - "or contains Python code errors." - ) diff --git a/pysatl_experiment/resolvers/iresolver.py b/pysatl_experiment/resolvers/iresolver.py deleted file mode 100644 index 7a6f9e6..0000000 --- a/pysatl_experiment/resolvers/iresolver.py +++ /dev/null @@ -1,297 +0,0 @@ -# pragma pylint: disable=attribute-defined-outside-init - -""" -This module load custom objects -""" - -import importlib.util -import inspect -import logging -import sys -from collections.abc import Iterator -from pathlib import Path -from typing import Any - -from pysatl_experiment.constants import Config -from pysatl_experiment.exceptions import OperationalException - - -logger = logging.getLogger(__name__) - - -class PathModifier: - def __init__(self, path: Path): - self.path = path - - def __enter__(self): - """Inject path to allow importing with relative imports.""" - sys.path.insert(0, str(self.path)) - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - """Undo insertion of local path.""" - str_path = str(self.path) - if str_path in sys.path: - sys.path.remove(str_path) - - -class IResolver: - """ - This class contains all the logic to load custom classes - """ - - # Child classes need to override this - object_type: type[Any] - object_type_str: str - user_subdir: str | None = None - initial_search_path: Path | None = None - # Optional a path (generator_path, report_generator_path) - extra_path: str | None = None - module_names: list[str] | None = None - - @classmethod - def build_search_paths( - cls, - user_data_dir: Path | None = None, - user_subdir: str | None = None, - extra_dirs: list[str] | None = None, - ) -> list[Path]: - abs_paths: list[Path] = [] - if cls.initial_search_path: - abs_paths.append(cls.initial_search_path) - - if user_subdir and user_data_dir: - abs_paths.insert(0, user_data_dir.joinpath(user_subdir)) - - # Add extra directory to the top of the search paths - if extra_dirs: - for directory in extra_dirs: - abs_paths.insert(0, Path(directory).resolve()) - - if cls.extra_path: - abs_paths.insert(0, Path(cls.extra_path).resolve()) - - return abs_paths - - @classmethod - def _get_valid_object(cls, module_path: Path, object_name: str | None, enum_failed: bool = False) -> Iterator[Any]: - """ - Generator returning objects with matching object_type and object_name in the path given. - :param module_path: absolute path to the module - :param object_name: Class name of the object - :param enum_failed: If True, will return None for modules which fail. - Otherwise, failing modules are skipped. - :return: generator containing tuple of matching objects - Tuple format: [Object, source] - """ - - # Generate spec based on absolute path - # Pass object_name as first argument to have logging print a reasonable name. - with PathModifier(module_path.parent): - module_name = module_path.stem or "" - spec = importlib.util.spec_from_file_location(module_name, str(module_path)) - if not spec: - return iter([None]) - - module = importlib.util.module_from_spec(spec) - try: - spec.loader.exec_module(module) # type: ignore # importlib does not use typehints - except ( - AttributeError, - ModuleNotFoundError, - SyntaxError, - ImportError, - NameError, - ) as err: - # Catch errors in case a specific module is not installed - logger.warning(f"Could not import {module_path} due to '{err}'") - if enum_failed: - return iter([None]) - - def is_valid_class(obj): - try: - return ( - inspect.isclass(obj) - and issubclass(obj, cls.object_type) - and obj is not cls.object_type - and obj.__module__ == module_name - ) - except TypeError: - return False - - valid_objects_gen = ( - (obj, inspect.getsource(module)) - for name, obj in inspect.getmembers(module, is_valid_class) - if (object_name is None or object_name == name) - ) - # The __module__ check ensures we only use strategies that are defined in this folder. - return valid_objects_gen - - @classmethod - def _search_object( - cls, directory: Path, *, object_name: str, add_source: bool = False - ) -> tuple[Any, Path] | tuple[None, None]: - """ - Search for the objectname in the given directory - :param directory: relative or absolute directory path - :param object_name: ClassName of the object to load - :return: object class - """ - logger.debug(f"Searching for {cls.object_type.__name__} {object_name} in '{directory}'") - for entry in directory.iterdir(): - # Only consider python files - if entry.suffix != ".py": - logger.debug("Ignoring %s", entry) - continue - if entry.is_symlink() and not entry.is_file(): - logger.debug("Ignoring broken symlink %s", entry) - continue - module_path = entry.resolve() - - obj = next(cls._get_valid_object(module_path, object_name), None) - - if obj: - obj[0].__file__ = str(entry) - if add_source: - obj[0].__source__ = obj[1] - return obj[0], module_path - return None, None - - @classmethod - def _load_object( - cls, - paths: list[Path], - *, - object_name: str, - add_source: bool = False, - kwargs: dict[str, Any] | None, - ) -> Any | None: - """ - Try to load object from path list. - """ - - for _path in paths: - try: - (module, module_path) = cls._search_object( - directory=_path, object_name=object_name, add_source=add_source - ) - if module: - logger.info( - f"Using resolved {cls.object_type.__name__.lower()[1:]} {object_name} from '{module_path}'..." - ) - return module(**kwargs) - except FileNotFoundError: - logger.warning('Path "%s" does not exist.', _path.resolve()) - - return None - - @classmethod - def _load_modules_object(cls, *, object_name: str, kwargs: dict[str, Any] | None) -> Any | None: - """ - Try to load object from path list. - """ - if cls.module_names is None: - return None - - for module_name in cls.module_names: - module_object = cls._load_module_object(object_name=object_name, kwargs=kwargs, module_name=module_name) - if module_object is not None: - return module_object - return None - - @classmethod - def _load_module_object(cls, *, object_name: str, kwargs: dict[str, Any] | None, module_name: str) -> Any | None: - """ - Try to load object from path list. - """ - - try: - module = getattr(importlib.import_module(module_name), object_name) - if module: - logger.info( - f"Using resolved {cls.object_type.__name__.lower()[1:]} {object_name} from '{module_name}'..." - ) - return module(**kwargs) - except FileNotFoundError: - logger.warning('Object "%s" does not exist.', object_name) - - return None - - @classmethod - def load_object( - cls, - object_name: str, - config: Config, - *, - kwargs: dict, - extra_dir: str | None = None, - ) -> Any: - """ - Search and loads the specified object as configured in the child class. - :param object_name: name of the module to import - :param config: configuration dictionary - :param extra_dir: additional directory to search for the given pairlist - :raises: OperationalException if the class is invalid or does not exist. - :return: Object instance or None - """ - - extra_dirs: list[str] = [] - if extra_dir: - extra_dirs.append(extra_dir) - - # TODO: fix - abs_paths = cls.build_search_paths(None, user_subdir=cls.user_subdir, extra_dirs=extra_dirs) - - found_object = cls._load_object(paths=abs_paths, object_name=object_name, kwargs=kwargs) - if found_object: - return found_object - raise OperationalException( - f"Impossible to load {cls.object_type_str} '{object_name}'. This class does not exist " - "or contains Python code errors." - ) - - @classmethod - def _build_rel_location(cls, directory: Path, entry: Path) -> str: - builtin = cls.initial_search_path == directory - return f"/{entry.relative_to(directory)}" if builtin else str(entry.relative_to(directory)) - - @classmethod - def _search_all_objects( - cls, - directory: Path, - enum_failed: bool, - recursive: bool = False, - basedir: Path | None = None, - ) -> list[dict[str, Any]]: - """ - Searches a directory for valid objects - :param directory: Path to search - :param enum_failed: If True, will return None for modules which fail. - Otherwise, failing modules are skipped. - :param recursive: Recursively walk directory tree searching for strategies - :return: List of dicts containing 'name', 'class' and 'location' entries - """ - logger.debug(f"Searching for {cls.object_type.__name__} '{directory}'") - objects: list[dict[str, Any]] = [] - if not directory.is_dir(): - logger.info(f"'{directory}' is not a directory, skipping.") - return objects - for entry in directory.iterdir(): - if recursive and entry.is_dir() and not entry.name.startswith("__") and not entry.name.startswith("."): - objects.extend(cls._search_all_objects(entry, enum_failed, recursive, basedir or directory)) - # Only consider python files - if entry.suffix != ".py": - logger.debug("Ignoring %s", entry) - continue - module_path = entry.resolve() - logger.debug(f"Path {module_path}") - for obj in cls._get_valid_object(module_path, object_name=None, enum_failed=enum_failed): - objects.append( - { - "name": obj[0].__name__ if obj is not None else "", - "class": obj[0] if obj is not None else None, - "location": entry, - "location_rel": cls._build_rel_location(basedir or directory, entry), - } - ) - return objects diff --git a/report_examples/critical_values_report.pdf b/report_examples/critical_values_report.pdf deleted file mode 100644 index b640a13..0000000 Binary files a/report_examples/critical_values_report.pdf and /dev/null differ diff --git a/report_examples/time_complexity_report.pdf b/report_examples/time_complexity_report.pdf deleted file mode 100644 index 2bb517e..0000000 Binary files a/report_examples/time_complexity_report.pdf and /dev/null differ diff --git a/pysatl_experiment/experiment/listener/__init__.py b/tests/__init__.py similarity index 100% rename from pysatl_experiment/experiment/listener/__init__.py rename to tests/__init__.py diff --git a/pysatl_experiment/experiment/report/__init__.py b/tests/core/distribution/__init__.py similarity index 100% rename from pysatl_experiment/experiment/report/__init__.py rename to tests/core/distribution/__init__.py diff --git a/tests/distribution/distribution_test.py b/tests/core/distribution/distribution_test.py similarity index 100% rename from tests/distribution/distribution_test.py rename to tests/core/distribution/distribution_test.py diff --git a/tests/core/store_test.py b/tests/core/store_test.py deleted file mode 100644 index 2710aca..0000000 --- a/tests/core/store_test.py +++ /dev/null @@ -1,34 +0,0 @@ -from pathlib import Path - -import pytest - -from pysatl_experiment.persistence.file_store.store import JsonStoreService - - -filename = "cache.json" - - -class TestJsonStoreService: - @pytest.fixture - def store(self): - return JsonStoreService(filename=filename) - - def teardown_method(self, method): - try: - Path(filename).unlink() - except OSError: - pass - - def test_get_empty(self, store): - assert store.get("a") is None - - def test_get_with_level_empty(self, store): - assert store.get_with_level(["a", "b", "c"]) is None - - def test_put(self, store): - store.put("a", 1) - assert store.get("a") == 1 - - def test_put_with_level(self, store): - store.put_with_level(["a", "b"], 2) - assert store.get_with_level(["a", "b"]) == 2 diff --git a/tests/distribution/__init__.py b/tests/distribution/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/listener_test.py b/tests/listener_test.py deleted file mode 100644 index 66043cc..0000000 --- a/tests/listener_test.py +++ /dev/null @@ -1,15 +0,0 @@ -from pysatl_experiment.experiment.listener.listeners import TimeEstimationListener - - -def test_time_estimation_before(): - listener = TimeEstimationListener() - listener.before() - assert listener.start_time is not None - - -def test_time_estimation_after(): - listener = TimeEstimationListener() - listener.before() - listener.after() - assert listener.start_time is not None - assert listener.end_time is not None diff --git a/tests/resolvers/__init__.py b/tests/resolvers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/store/__init__.py b/tests/store/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/store/benchmark_result_db_test.py b/tests/store/benchmark_result_db_test.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/store/critical_value_db_test.py b/tests/store/critical_value_db_test.py deleted file mode 100644 index dc7c395..0000000 --- a/tests/store/critical_value_db_test.py +++ /dev/null @@ -1,41 +0,0 @@ -from pathlib import Path - -import numpy as np -import pytest - -from pysatl_experiment.persistence.db_store import CriticalValueDbStore - - -store_name = "pysatl.sqlite" - - -class TestCriticalValueSqLiteStoreService: - @pytest.fixture - def store(self): - store = CriticalValueDbStore(db_url="sqlite:///" + store_name) - store.init() - return store - - def teardown_method(self, method): - try: - Path(store_name).unlink() - Path(store_name + "-journal").unlink() - except OSError: - pass - - def test_get_critical_value_empty(self, store): - assert store.get_critical_value("", 5, 0.05) is None - - def test_get_distribution_empty(self, store): - assert store.get_distribution("", 5) is None - - def test_get_critical_value(self, store): - store.insert_critical_value("test", 2, 0.05, 1.5) - value = store.get_critical_value("test", 2, 0.05) - assert value == 1.5 - - def test_get_distribution(self, store): - store.insert_distribution("gen_code1", 4, [0.1, 0.2, 0.3, 0.4]) - ar = np.array(store.get_distribution("gen_code1", 4)) - expected = np.array([0.1, 0.2, 0.3, 0.4]) - assert np.array_equal(ar, expected) diff --git a/tests/store/critical_value_file_store_test.py b/tests/store/critical_value_file_store_test.py deleted file mode 100644 index af45506..0000000 --- a/tests/store/critical_value_file_store_test.py +++ /dev/null @@ -1,40 +0,0 @@ -import shutil - -import numpy as np -import pytest - -from pysatl_experiment.persistence.file_store import CriticalValueFileStore - - -store_name = "data5" - - -class TestCriticalValueFileStoreService: - @pytest.fixture - def store(self): - store = CriticalValueFileStore(path=store_name) - store.init() - return store - - def teardown_method(self, method): - try: - shutil.rmtree(store_name) - except OSError: - pass - - def test_get_critical_value_empty(self, store): - assert store.get_critical_value("", 5, 0.05) is None - - def test_get_distribution_empty(self, store): - assert store.get_distribution("", 5) is None - - def test_get_critical_value(self, store): - store.insert_critical_value("test", 2, 0.05, 1.5) - value = store.get_critical_value("test", 2, 0.05) - assert value == 1.5 - - def test_get_distribution(self, store): - store.insert_distribution("gen_code1", 4, [0.1, 0.2, 0.3, 0.4]) - ar = np.array(store.get_distribution("gen_code1", 4)) - expected = np.array([0.1, 0.2, 0.3, 0.4]) - assert np.array_equal(ar, expected) diff --git a/tests/store/result_sql_lite_test.py b/tests/store/result_sql_lite_test.py deleted file mode 100644 index dc35252..0000000 --- a/tests/store/result_sql_lite_test.py +++ /dev/null @@ -1,53 +0,0 @@ -from pathlib import Path - -import numpy as np -import pytest - -from pysatl_experiment.experiment.configuration import TestWorkerResult -from pysatl_experiment.persistence.db_store import ResultDbStore - - -store_name = "pysatl.sqlite" - - -class TestResult(TestWorkerResult): - __test__ = False - - def __init__(self, name, values): - self.name = name - self.values = values - - -class TestBenchmarkResultSqLiteStoreService: - @pytest.fixture - def store(self): - store = ResultDbStore(db_url="sqlite:///" + store_name) - store.init() - return store - - def teardown_method(self): - try: - Path(store_name).unlink() - Path(store_name + "-journal").unlink() - except OSError: - pass - - def test_get_results_empty(self, store): - assert len(store.get_results(0, 5)) == 0 - - def test_get_result_empty(self, store): - assert store.get_result("test") is None - - def test_get_result_value(self, store): - store.insert_result("test", TestResult("name", [0.1, 0.5])) - result = store.get_result("test") - assert result.name == "name" - assert np.array_equal(result.values, [0.1, 0.5]) - - def test_get_results_value(self, store): - store.insert_result("test", TestResult("name1", [0.1, 0.5])) - store.insert_result("test1", TestResult("name", [0.3, 0.2])) - result = store.get_results(0, 5) - result = sorted(list(map(lambda x: x.name, result))) - assert result[0] == "name" - assert result[1] == "name1" diff --git a/tests/store/rvs_db_test.py b/tests/store/rvs_db_test.py deleted file mode 100644 index 7d1cfd7..0000000 --- a/tests/store/rvs_db_test.py +++ /dev/null @@ -1,53 +0,0 @@ -from pathlib import Path - -import numpy as np -import pytest - -from pysatl_experiment.persistence.db_store import RvsDbStore - - -store_name = "pysatl.sqlite" - - -class TestRvsSqLiteStoreService: - @pytest.fixture - def store(self): - store = RvsDbStore(db_url="sqlite:///" + store_name) - store.init() - return store - - def teardown_method(self): - try: - Path(store_name).unlink() - Path(store_name + "-journal").unlink() - except OSError: - pass - - def test_get_rvs_stat_empty(self, store): - assert len(store.get_rvs_stat()) == 0 - - def test_get_rvs_empty(self, store): - assert len(store.get_rvs("test", 2)) == 0 - - def test_insert_rvs(self, store): - store.insert_rvs("gen_code", 10, [0.1, 0.2]) - ar = np.array(store.get_rvs_stat()) - expected = np.array([("gen_code", 10, 1)]) - assert np.array_equal(ar, expected) - - def test_insert_all_rvs(self, store): - store.insert_all_rvs("gen_code1", 2, [[0.1, 0.2], [0.3, 0.4]]) - ar = np.array(store.get_rvs_stat()) - expected = np.array([("gen_code1", 2, 2)]) - assert np.array_equal(ar, expected) - - def test_get_rvs(self, store): - store.insert_all_rvs("gen_code2", 2, [[0.1, 0.2], [0.3, 0.4]]) - ar = np.array(store.get_rvs("gen_code2", 2)) - expected = np.array([[0.1, 0.2], [0.3, 0.4]]) - assert np.array_equal(ar, expected) - - def test_clear_rvs(self, store): - store.insert_all_rvs("gen_code2", 2, [[0.1, 0.2], [0.3, 0.4]]) - store.clear_all_rvs() - assert len(store.get_rvs("gen_code2", 2)) == 0 diff --git a/tests/store/rvs_file_store_test.py b/tests/store/rvs_file_store_test.py deleted file mode 100644 index 80c9ca9..0000000 --- a/tests/store/rvs_file_store_test.py +++ /dev/null @@ -1,52 +0,0 @@ -import shutil - -import numpy as np -import pytest - -from pysatl_experiment.persistence.file_store.rvs_store import RvsFileStore - - -store_name = "data5" - - -class TestRvsSqlLiteStoreService: - @pytest.fixture - def store(self): - store = RvsFileStore(path=store_name) - store.init() - return store - - def teardown_method(self, method): - try: - shutil.rmtree(store_name) - except OSError: - pass - - def test_get_rvs_stat_empty(self, store): - assert len(store.get_rvs_stat()) == 0 - - def test_get_rvs_empty(self, store): - assert len(store.get_rvs("test", 2)) == 0 - - def test_insert_rvs(self, store): - store.insert_rvs("gen_code", 10, [0.1, 0.2]) - ar = np.array(store.get_rvs_stat()) - expected = np.array([("gen_code", 10, 1)]) - assert np.array_equal(ar, expected) - - def test_insert_all_rvs(self, store): - store.insert_all_rvs("gen_code1", 2, [[0.1, 0.2], [0.3, 0.4]]) - ar = np.array(store.get_rvs_stat()) - expected = np.array([("gen_code1", 2, 2)]) - assert np.array_equal(ar, expected) - - def test_get_rvs(self, store): - store.insert_all_rvs("gen_code2", 2, [[0.1, 0.2], [0.3, 0.4]]) - ar = np.array(store.get_rvs("gen_code2", 2)) - expected = np.array([[0.1, 0.2], [0.3, 0.4]]) - assert np.array_equal(ar, expected) - - def test_clear_rvs(self, store): - store.insert_all_rvs("gen_code2", 2, [[0.1, 0.2], [0.3, 0.4]]) - store.clear_all_rvs() - assert len(store.get_rvs("gen_code2", 2)) == 0 diff --git a/weibull_experiment.py b/weibull_experiment.py deleted file mode 100644 index 5645122..0000000 --- a/weibull_experiment.py +++ /dev/null @@ -1,157 +0,0 @@ -import multiprocessing - -from pysatl_criterion.statistics.weibull import ( - AndersonDarlingWeibullGofStatistic, - Chi2PearsonWeibullGofStatistic, - CrammerVonMisesWeibullGofStatistic, - KolmogorovSmirnovWeibullGofStatistic, - KullbackLeiblerWeibullGofStatistic, - LaplaceTransform2WeibullGofStatistic, - LaplaceTransform3WeibullGofStatistic, - LaplaceTransformWeibullGofStatistic, - LiaoShimokawaWeibullGofStatistic, - LillieforsWeibullGofStatistic, - LOSWeibullGofStatistic, - MahdiDoostparastWeibullGofStatistic, - MinToshiyukiWeibullGofStatistic, - MSFWeibullGofStatistic, - OKWeibullGofStatistic, - REJGWeibullGofStatistic, - RSBWeibullGofStatistic, - SBWeibullGofStatistic, - ST1WeibullGofStatistic, - ST2WeibullGofStatistic, - TikuSinghWeibullGofStatistic, - WatsonWeibullGofStatistic, -) -from pysatl_experiment.experiment import Experiment -from pysatl_experiment.experiment.configuration.configuration import ( - AlternativeConfiguration, - ExperimentConfiguration, - ReportConfiguration, - TestConfiguration, -) -from pysatl_experiment.experiment.generator import WeibullGenerator -from pysatl_experiment.experiment.generator.generators import ( - ExponentialGenerator, - GammaGenerator, - GompertzGenerator, - InvGaussGenerator, - LognormGenerator, - RiceGenerator, -) -from pysatl_experiment.experiment.hypothesis import WeibullHypothesis -from pysatl_experiment.experiment.listener.listeners import TimeEstimationListener -from pysatl_experiment.experiment.report.model import PdfPowerReportBuilder -from pysatl_experiment.experiment.test.worker import PowerCalculationWorker -from pysatl_experiment.persistence.db_store import CriticalValueDbStore, RvsDbStore -from pysatl_experiment.persistence.db_store.result_store import ResultDbStore - - -if __name__ == "__main__": - print("Start Weibull experiment") - - # Configuring experiment - test_data_tel = TimeEstimationListener() - generate_data_tel = TimeEstimationListener() - - db_url = "sqlite:///weibull_experiment.sqlite" - listeners = [generate_data_tel] - hypothesis = WeibullHypothesis() - test_threads = multiprocessing.cpu_count() - generation_threads = multiprocessing.cpu_count() - sizes = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000] - - critical_value_store = CriticalValueDbStore(db_url=db_url) - rvs_store = RvsDbStore(db_url=db_url) - result_store = ResultDbStore(db_url=db_url) - - alternatives = [ - ExponentialGenerator(lam=0.5), - ExponentialGenerator(lam=1), - ExponentialGenerator(lam=1.5), - GammaGenerator(alfa=1, beta=2), - GammaGenerator(alfa=2, beta=2), - GammaGenerator(alfa=3, beta=2), - GammaGenerator(alfa=5, beta=1), - GammaGenerator(alfa=9, beta=0.5), - GammaGenerator(alfa=0.5, beta=1), - InvGaussGenerator(mu=1, lam=0.2), - InvGaussGenerator(mu=1, lam=1), - InvGaussGenerator(mu=1, lam=3), - InvGaussGenerator(mu=3, lam=0.2), - InvGaussGenerator(mu=3, lam=1), - LognormGenerator(mu=0, s=0.25), - LognormGenerator(mu=0, s=1), - LognormGenerator(mu=0, s=5), - LognormGenerator(mu=5, s=0.25), - LognormGenerator(mu=5, s=1), - LognormGenerator(mu=5, s=5), - RiceGenerator(nu=0, sigma=1), - RiceGenerator(nu=0.5, sigma=1), - RiceGenerator(nu=4, sigma=1), - GompertzGenerator(eta=0.1, b=1), - GompertzGenerator(eta=2, b=1), - GompertzGenerator(eta=3, b=1), - GompertzGenerator(eta=1, b=2), - GompertzGenerator(eta=1, b=3), - WeibullGenerator(l=1, k=5), - WeibullGenerator(l=2, k=5), - ] - tests = [ - KolmogorovSmirnovWeibullGofStatistic(), - MinToshiyukiWeibullGofStatistic(), - Chi2PearsonWeibullGofStatistic(), - CrammerVonMisesWeibullGofStatistic(), - ST1WeibullGofStatistic(), - ST2WeibullGofStatistic(), - TikuSinghWeibullGofStatistic(), - LOSWeibullGofStatistic(), - MSFWeibullGofStatistic(), - OKWeibullGofStatistic(), - SBWeibullGofStatistic(), - RSBWeibullGofStatistic(), - LaplaceTransform3WeibullGofStatistic(), - LaplaceTransform2WeibullGofStatistic(), - LaplaceTransformWeibullGofStatistic(), - KullbackLeiblerWeibullGofStatistic(), - LiaoShimokawaWeibullGofStatistic(), - WatsonWeibullGofStatistic(), - MahdiDoostparastWeibullGofStatistic(), - REJGWeibullGofStatistic(), - AndersonDarlingWeibullGofStatistic(), - LillieforsWeibullGofStatistic(), - ] - - alternatives_configuration = AlternativeConfiguration( - alternatives, - sizes, - count=1_000, - threads=generation_threads, - listeners=listeners, - skip_step=True, - ) - - power_calculation_worker = PowerCalculationWorker(0.05, 100_000, critical_value_store, hypothesis=hypothesis) - test_configuration = TestConfiguration( - tests, - threads=test_threads, - worker=power_calculation_worker, - listeners=[test_data_tel], - skip_step=True, - ) - - report_builder = PdfPowerReportBuilder() - report_configuration = ReportConfiguration(report_builder) - - experiment_configuration = ExperimentConfiguration( - alternatives_configuration, - test_configuration, - report_configuration, - rvs_store=rvs_store, - result_store=result_store, - ) - experiment = Experiment(experiment_configuration) - - # Execute experiment - experiment.execute()