diff --git a/src/adaptive_instance_selection.py b/src/adaptive_instance_selection.py deleted file mode 100644 index 8f07c84c93f80cb00918aad99a87ea8e64384459..0000000000000000000000000000000000000000 --- a/src/adaptive_instance_selection.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) 2024 - Zizhe Wang -# https://zizhe.wang - -#################################### -# # -# AUTOMATIC SEARCH SPACE REDUCTION # -# # -#################################### - -import numpy as np -from sklearn.cluster import KMeans -from scipy.stats.qmc import LatinHypercube as lhs -from config import PARAM_TYPES, PARAM_BOUNDS - -# Initial Sampling -def initial_sampling(param_bounds, n_samples): - dimensions = len(param_bounds) - samples = lhs(d=dimensions).random(n=n_samples) # Latin hypercube sampling (LHS) - if samples.size == 0: - raise ValueError("Initial sampling produced an empty set of samples.") - - # Scale samples to parameter bounds and respect parameter types - for i, (param, bounds_info) in enumerate(param_bounds.items()): - bounds = bounds_info["bounds"] - samples[:, i] = bounds[0] + samples[:, i] * (bounds[1] - bounds[0]) - if bounds_info["type"] == 'int': - samples[:, i] = np.round(samples[:, i]).astype(int) - - print(f"Initial samples shape: {samples.shape}") - return samples - -# Evaluate Samples -def evaluate_samples(samples, objective_function): - results = [] - for sample in samples: - result = objective_function(sample) - results.append(result) - return np.array(results) - -# Advanced Clustering -def advanced_clustering_samples(samples, n_clusters): - if len(samples) == 0: - raise ValueError("Cannot cluster an empty set of samples.") - kmeans = KMeans(n_clusters=n_clusters, random_state=0, n_init=10) - kmeans.fit(samples) - labels = kmeans.predict(samples) - centers = kmeans.cluster_centers_ - - return labels, centers - -# Adaptive Selection with Adaptive Threshold -def adaptive_select_informative_instances(samples, results, initial_threshold=0.05, adapt_rate=0.01, desired_samples=None, max_iterations=100): - if len(samples) == 0 or len(results) == 0: - raise ValueError("Received empty samples or results for selection.") - - performance = np.nanmean(results, axis=1) # Use np.nanmean to ignore nan values - threshold = initial_threshold - iteration = 0 - - while iteration < max_iterations: - iteration += 1 - print(f"Iteration {iteration}: Current threshold: {threshold}") - - # Cap the threshold at 1.0 - effective_threshold = min(threshold, 1.0) - cutoff = np.nanpercentile(performance, effective_threshold * 100) # Use np.nanpercentile to ignore nan values - selected_samples = samples[performance <= cutoff] - - print(f"Iteration {iteration}: Number of selected samples: {len(selected_samples)}") - - if desired_samples is not None and len(selected_samples) >= desired_samples: - print(f"Iteration {iteration}: Desired number of samples reached.") - break - if len(selected_samples) == len(samples): - print(f"Iteration {iteration}: All samples selected.") - break - threshold += adapt_rate - - if iteration == max_iterations: - print(f"Final threshold after max iterations: {threshold}") - print(f"Performance values: {performance}") - print(f"Number of selected samples: {len(selected_samples)}") - if desired_samples is not None and len(selected_samples) < desired_samples: - print("Falling back to the best available samples.") - # Select the top desired_samples samples based on performance - best_indices = np.argsort(performance)[:desired_samples] - selected_samples = samples[best_indices] - - if selected_samples.size == 0: - raise ValueError("Selection of informative instances resulted in an empty set.") - - print(f"Final selected samples shape: {selected_samples.shape}") - return selected_samples[:desired_samples] # Ensure the number of selected samples matches the desired number - -# Iterative Refinement -# Iterative Refinement -def iterative_refinement(samples, results, objective_function, maximize_indices, n_iterations=2, initial_threshold=0.10, adapt_rate=0.03): - for iteration in range(n_iterations): - print(f"Iteration {iteration}: Starting with samples shape: {samples.shape}") - - # Evaluate current samples - current_results = evaluate_samples(samples, objective_function) - - # Print performance metrics for current samples - print(f"Iteration {iteration}: Current results: {current_results}") - - # Select informative instances with adaptive threshold - selected_samples = adaptive_select_informative_instances(samples, current_results, initial_threshold, adapt_rate) - - # Ensure objective negation is correctly handled - for i in range(len(current_results)): - for idx in maximize_indices: - if not np.isnan(current_results[i][idx]): - current_results[i][idx] = -current_results[i][idx] - - # Ensure at least a minimum number of samples are selected to maintain diversity - if len(selected_samples) < 3: - selected_samples = samples[np.argsort(np.nanmean(current_results, axis=1))[:3]] - - # Re-cluster the selected samples - n_clusters = max(1, min(2, int(len(selected_samples) * 0.2))) # Ensure at least 1 cluster, maximum 2 clusters - labels, centers = advanced_clustering_samples(selected_samples, n_clusters) - - # Generate new samples around cluster centers - new_samples = [] - for center in centers: - for _ in range(max(1, (len(samples) - len(selected_samples)))): # Control the number of new samples - perturbations = np.random.uniform(-0.03, 0.03, center.shape) # Use smaller perturbations for finer adjustments - new_samples.append(center + perturbations) - - # Combine selected samples with new samples, ensuring we don't grow the sample size too much - combined_samples = np.vstack((selected_samples, new_samples)) - samples = combined_samples[:len(selected_samples) + (len(samples) - len(selected_samples))] # Ensure the sample size matches the original size - - # Debugging output - print(f"Iteration {iteration}: Samples shape after selection and new sample generation: {samples.shape}") - - return samples - -def generate_new_samples(existing_samples, pop_size, n_adaptive_samples): - n_new_samples = pop_size - n_adaptive_samples - new_samples = initial_sampling(PARAM_BOUNDS, n_new_samples) - combined_samples = np.vstack((existing_samples, new_samples)) - return combined_samples \ No newline at end of file diff --git a/src/config.json b/src/config.json index b64bc657f8d3968677e8fc623a44de4ef5703a40..d30634fff58f766fe9d73343c1f29af67214df3b 100644 --- a/src/config.json +++ b/src/config.json @@ -1,35 +1,34 @@ { - "MODEL_NAME": "SimpleHeatingSystem", - "MODEL_FILE": "SimpleHeatingSystem.mo", - "SIMULATION_STOP_TIME": 3000, + "MODEL_NAME": "ITSystem", + "MODEL_FILE": "ITSystem.mo", + "SIMULATION_STOP_TIME": 100, "PRECISION": 2, - "PARAMETERS": ["Q_max", "T_set"], + "PARAMETERS": ["activeCores", "cpuFrequency"], "OBJECTIVES": [ - {"name": "energy", "maximize": false}, - {"name": "comfort", "maximize": true} + {"name": "remainingEnergy", "maximize": true}, + {"name": "performance", "maximize": true} ], "PARAM_BOUNDS": { - "Q_max": { - "bounds": [1000, 5000], + "activeCores": { + "bounds": [1, 4], "type": "int" }, - "T_set": { - "bounds": [280, 310], - "type": "int" + "cpuFrequency": { + "bounds": [1.0, 3.0], + "type": "float" } }, "OPTIMIZATION_CONFIG": { - "USE_ADAPTIVE_INSTANCE_SELECTION": true, - "ADAPTIVE_INSTANCE_SELECTION_FREQUENCY": 5, - "ALGORITHM_NAME": "NSGA2", - "POP_SIZE": 10, - "MIN_POP_SIZE": 1, - "N_GEN": 10 + "USE_SINGLE_OBJECTIVE": false, + "ALGORITHM_NAME": "nsga2", + "POP_SIZE": 5, + "N_GEN": 1 }, "PLOT_CONFIG": { "PLOT_X": "Energy Consumption", "PLOT_Y": "Comfort", - "PLOT_TITLE": "Pareto Front of Energy Consumption vs Comfort" + "PLOT_TITLE": "Pareto Front of Energy Consumption vs Comfort", + "ENABLE_PLOT": false }, "N_JOBS": -1 } \ No newline at end of file diff --git a/src/optimization_libraries.py b/src/optimization_libraries.py index 139f15932eab94e636dc6b00d7eb8767f6afa8d5..ac79e1f91ae5fefb90f3bf260a7be159700fb980 100644 --- a/src/optimization_libraries.py +++ b/src/optimization_libraries.py @@ -24,6 +24,9 @@ def initialize_algorithm(algorithm_name, pop_size=None): if isinstance(attribute, type): # Check if it's a class algorithm_modules[attribute_name.lower()] = attribute + # Print the available algorithms for debugging + # print("Available algorithms:", list(algorithm_modules.keys())) + # Check if the algorithm name is in the imported modules if algorithm_name.lower() not in algorithm_modules: raise ValueError(f"Algorithm {algorithm_name} is not supported.") diff --git a/src/parallel_computing.py b/src/parallel_computing.py index 4bd1ff2ec42641c60ed7c5ffbc02f0dc49eed7a2..83e7302934c2f2c9f1987257d0279fc2248f5989 100644 --- a/src/parallel_computing.py +++ b/src/parallel_computing.py @@ -14,8 +14,7 @@ import numpy as np from time import sleep from joblib import Parallel, delayed from OMPython import OMCSessionZMQ -from config import MODEL_FILE, MODEL_NAME, SIMULATION_STOP_TIME, PARAMETERS, OBJECTIVE_NAMES, PARAM_BOUNDS, PARAM_TYPES, MODEL_PATH, PRECISION, OPTIMIZATION_CONFIG, N_JOBS -from adaptive_instance_selection import initial_sampling, evaluate_samples, advanced_clustering_samples, adaptive_select_informative_instances, iterative_refinement +from config import MODEL_FILE, MODEL_NAME, SIMULATION_STOP_TIME, PARAMETERS, OBJECTIVE_NAMES, PARAM_BOUNDS, PARAM_TYPES, MODEL_PATH, PRECISION, N_JOBS temp_dirs = [] # List to store paths of temporary directories @@ -98,7 +97,7 @@ def optimization_function(param_values, retries=3, delay=2): shutdown_omc(omc) # If all attempts fail, return NaNs - return [np.nan] * len(OBJECTIVES) + return [np.nan] * len(OBJECTIVE_NAMES) def shutdown_omc(omc): try: @@ -125,29 +124,8 @@ def cleanup_temp_dirs(): print(f"Error: {e}") break # Exit the loop for non-permission errors -def execute_parallel_tasks(tasks, use_adaptive_instance_selection, maximize_indices): - results = [] - - if use_adaptive_instance_selection: - # Initial sampling - initial_samples = initial_sampling(PARAM_BOUNDS, OPTIMIZATION_CONFIG['POP_SIZE']) - - # Parallel evaluation of initial samples - initial_results = Parallel(n_jobs=N_JOBS)(delayed(optimization_function)(sample) for sample in initial_samples) - - # Iterative refinement - refined_samples = iterative_refinement(initial_samples, initial_results, optimization_function, maximize_indices) - - # Parallel evaluation of refined samples - refined_results = Parallel(n_jobs=N_JOBS)(delayed(optimization_function)(task) for task in refined_samples) - - # Combine initial and refined results, ensuring the number matches the initial parameter sets - results = initial_results + refined_results - - # Ensure only the first `len(tasks)` results are considered - results = results[:len(tasks)] - else: - results = Parallel(n_jobs=N_JOBS)(delayed(optimization_function)(task) for task in tasks) +def execute_parallel_tasks(tasks): + results = Parallel(n_jobs=N_JOBS)(delayed(optimization_function)(task) for task in tasks) # Ensure results length matches tasks length by handling exceptions completed_results = [result for result in results if result is not None]