diff --git a/.github/workflows/unit_testing_linux.yml b/.github/workflows/unit_testing_linux.yml index 1b03fd0..15cd0f5 100644 --- a/.github/workflows/unit_testing_linux.yml +++ b/.github/workflows/unit_testing_linux.yml @@ -11,10 +11,10 @@ on: jobs: test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: matrix: - python-version: [ '3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13' ] + python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', '3.13' ] steps: - uses: actions/checkout@v4 diff --git a/src/quocslib/__init__.py b/src/quocslib/__init__.py index 26745d7..e4dd833 100644 --- a/src/quocslib/__init__.py +++ b/src/quocslib/__init__.py @@ -1,3 +1,3 @@ from __future__ import absolute_import, division, print_function, unicode_literals -__VERSION__ = "0.0.63" \ No newline at end of file +__VERSION__ = "0.0.64" \ No newline at end of file diff --git a/src/quocslib/communication/AllInOneCommunication.py b/src/quocslib/communication/AllInOneCommunication.py index 0db4b34..ad6d10b 100644 --- a/src/quocslib/communication/AllInOneCommunication.py +++ b/src/quocslib/communication/AllInOneCommunication.py @@ -16,6 +16,7 @@ import os import time +import datetime from typing import List from quocslib.utils.AbstractFoM import AbstractFoM @@ -59,7 +60,7 @@ def __init__(self, # Pre job name pre_job_name = interface_job_name # Datetime for 1-1 association - self.date_time = str(time.strftime("%Y%m%d_%H%M%S")) + self.date_time = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f") # Client job name to send to the Server self.client_job_name = self.date_time + "_" + pre_job_name ### @@ -67,10 +68,20 @@ def __init__(self, ### # Optimization folder optimization_folder = "QuOCS_Results" - self.results_path = os.path.join(os.getcwd(), optimization_folder, self.client_job_name) if not os.path.isdir(os.path.join(os.getcwd(), optimization_folder)): os.makedirs(os.path.join(os.getcwd(), optimization_folder)) # Create the folder for logging and results + self.results_path = os.path.join(os.getcwd(), optimization_folder, self.client_job_name) + # # check if folder already exists + # if os.path.isdir(self.results_path): + # # if it exists, include microseconds of time + # self.date_time = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f") + # self.client_job_name = self.date_time + "_" + pre_job_name + # self.results_path = os.path.join(os.getcwd(), optimization_folder, self.client_job_name) + # check if it already exists + if os.path.isdir(self.results_path): + raise FileExistsError("The folder {0} already exists. Please change the job name.".format(self.results_path)) + # Create the folder for logging and results os.makedirs(self.results_path) # Write the current quocs lib version in the file with open(os.path.join(self.results_path, "quocs_version.txt"), "w") as version_file: @@ -81,7 +92,6 @@ def __init__(self, self.print_general_log = True # Figure of merit object self.FoM_obj = FoM_obj - # TODO Thinks whether it is a good idea dumping the results # Dumping data object self.dump_obj = dump_attribute(self.results_path, self.date_time, dump_format) # Handle exit object diff --git a/src/quocslib/optimizationalgorithms/ADAlgorithm.py b/src/quocslib/optimizationalgorithms/ADAlgorithm.py index 4d2e719..6be52c4 100644 --- a/src/quocslib/optimizationalgorithms/ADAlgorithm.py +++ b/src/quocslib/optimizationalgorithms/ADAlgorithm.py @@ -77,13 +77,13 @@ def __init__(self, optimization_dict: dict = None, communication_obj=None, FoM_o self.rng = RandomNumberGenerator(seed_number=seed_number) except (TypeError, KeyError): default_seed_number = np.random.randint(0, 10000) - message = "Seed number must be an integer value. Set {0} as a seed numer for this optimization".format( + message = "Seed number must be an integer value. Set {0} as a seed number for this optimization".format( default_seed_number) self.rng = RandomNumberGenerator(seed_number=default_seed_number) self.comm_obj.print_logger(message, level=30) else: default_seed_number = np.random.randint(0, 10000) - message = "Seed number must be an integer value. Set {0} as a seed numer for this optimization".format( + message = "Seed number must be an integer value. Set {0} as a seed number for this optimization".format( default_seed_number) self.rng = RandomNumberGenerator(seed_number=default_seed_number) self.comm_obj.print_logger(message, level=30) diff --git a/src/quocslib/optimizationalgorithms/GRAPEAlgorithm.py b/src/quocslib/optimizationalgorithms/GRAPEAlgorithm.py index d392298..598fe38 100644 --- a/src/quocslib/optimizationalgorithms/GRAPEAlgorithm.py +++ b/src/quocslib/optimizationalgorithms/GRAPEAlgorithm.py @@ -73,7 +73,7 @@ def __init__(self, optimization_dict: dict = None, communication_obj=None, FoM_o seed_number = alg_parameters["random_number_generator"]["seed_number"] except (TypeError, KeyError): seed_number = 2022 - message = "Seed number must be an integer value. Set {0} as a seed numer for this optimization".format( + message = "Seed number must be an integer value. Set {0} as a seed number for this optimization".format( seed_number) self.comm_obj.print_logger(message, level=30) self.rng = RandomNumberGenerator(seed_number=seed_number) diff --git a/src/quocslib/optimizationalgorithms/dCRABAlgorithm.py b/src/quocslib/optimizationalgorithms/dCRABAlgorithm.py index 53f0d2a..05e2725 100644 --- a/src/quocslib/optimizationalgorithms/dCRABAlgorithm.py +++ b/src/quocslib/optimizationalgorithms/dCRABAlgorithm.py @@ -112,7 +112,7 @@ def __init__(self, optimization_dict: dict = None, communication_obj=None, **kwa self.rng = RandomNumberGenerator(seed_number=seed_number) except (TypeError, KeyError): default_seed_number = 2022 - message = "Seed number must be an integer value. Set {0} as a seed numer for this optimization".format( + message = "Seed number must be an integer value. Set {0} as a seed number for this optimization".format( default_seed_number) self.rng = RandomNumberGenerator(seed_number=default_seed_number) self.comm_obj.print_logger(message, level=30) diff --git a/tests/test_quick_parallel_jobs.py b/tests/test_quick_parallel_jobs.py new file mode 100644 index 0000000..1865641 --- /dev/null +++ b/tests/test_quick_parallel_jobs.py @@ -0,0 +1,66 @@ +from quocslib.utils.AbstractFoM import AbstractFoM +from quocslib.utils.inputoutput import readjson +from quocslib.Optimizer import Optimizer +from scipy.optimize import rosen +import numpy as np +import os +import pytest +import threading + + +class RosenFoM(AbstractFoM): + def __init__(self, args_dict: dict = None): + if args_dict is None: + args_dict = {} + + self.FoM_list = [] + self.param_list = [] + self.save_path = "" + + def save_FoM(self): + np.savetxt(os.path.join(self.save_path, 'FoM.txt'), self.FoM_list) + np.savetxt(os.path.join(self.save_path, 'params.txt'), self.param_list) + + def set_save_path(self, save_path: str = ""): + self.save_path = save_path + + def get_FoM(self, pulses: list = [], parameters: list = [], timegrids: list = []) -> dict: + + FoM = rosen(np.asarray(parameters)) + + self.FoM_list.append(FoM) + self.param_list.append(parameters) + + return {"FoM": FoM} + + +def main(optimization_dictionary: dict): + + FoM_object = RosenFoM() + + optimization_obj = Optimizer(optimization_dictionary, FoM_object) + + FoM_object.set_save_path(optimization_obj.results_path) + + optimization_obj.execute() + + FoM_object.save_FoM() + + +def test_parameter_optimization(): + # get the optimization settings from the json dictionary + folder = os.path.dirname(os.path.realpath(__file__)) + optimization_dictionary = readjson(os.path.join(folder, "opt_Rosen_NM.json")) + opt_dict_1 = optimization_dictionary.copy() + opt_dict_2 = optimization_dictionary.copy() + opt_dict_1["optimization_client_name"] = "Check_quick_parallel_job_1" + opt_dict_2["optimization_client_name"] = "Check_quick_parallel_job_2" + + t1 = threading.Thread(target=main, args=(opt_dict_1, )) + t2 = threading.Thread(target=main, args=(opt_dict_2, )) + + t1.start() + t2.start() + + t1.join(timeout=10) + t2.join(timeout=10)