@@ -2299,24 +2299,27 @@ def save_experiment(self, filename=None) -> None:
22992299 """
23002300 Save the experiment to a file.
23012301 """
2302- # check if the key "spot_writer" is in the fun_control dictionary
2303- # remove the key "spot_writer" from the fun_control dictionary,
2304- # because it is not serializable.
2305- # TODO: It will be re-added when the experiment is loaded.
2302+ # Remove or close any unpickleable objects, e.g., the spot_writer
23062303 self .close_and_del_spot_writer ()
23072304
2305+ # Remove the logger handler before pickling
2306+ self .remove_logger_handlers ()
2307+
2308+ # Create deep copies of control dictionaries
23082309 fun_control = copy .deepcopy (self .fun_control )
23092310 optimizer_control = copy .deepcopy (self .optimizer_control )
23102311 surrogate_control = copy .deepcopy (self .surrogate_control )
23112312 design_control = copy .deepcopy (self .design_control )
2312- # try to copy the spot_tuner object. If not possible,issue a warning and set spot_tuner to None.
2313+
2314+ # Deep copy the spot object itself (except unpickleable components)
23132315 try :
23142316 spot_tuner = copy .deepcopy (self )
23152317 except Exception as e :
23162318 print ("Warning: Could not copy spot_tuner object!" )
2317- logger .warning ("Warning: Could not copy spot_tuner object!" )
2318- logger .warning (f"Error: { e } " )
2319+ print (f"Error: { e } " )
23192320 spot_tuner = self
2321+
2322+ # Prepare the experiment dictionary
23202323 experiment = {
23212324 "design_control" : design_control ,
23222325 "fun_control" : fun_control ,
@@ -2325,26 +2328,41 @@ def save_experiment(self, filename=None) -> None:
23252328 "surrogate_control" : surrogate_control ,
23262329 }
23272330
2328- PREFIX = fun_control ["PREFIX" ]
2331+ # Determine the filename based on PREFIX if not provided
2332+ PREFIX = fun_control .get ("PREFIX" )
23292333 if filename is None and PREFIX is not None :
23302334 filename = get_experiment_filename (PREFIX )
2335+
2336+ # Serialize the experiment dictionary to the pickle file
23312337 if filename is not None :
23322338 with open (filename , "wb" ) as handle :
23332339 try :
23342340 pickle .dump (experiment , handle , protocol = pickle .HIGHEST_PROTOCOL )
23352341 except Exception as e :
2336- logger .error (f"Error: { e } " )
2337- pprint .pprint (fun_control )
2338- print ("design_control:" )
2339- pprint .pprint (design_control )
2340- print ("optimizer_control:" )
2341- pprint .pprint (optimizer_control )
2342- print ("surrogate_control:" )
2343- pprint .pprint (surrogate_control )
2344- print ("spot_tuner:" )
2345- pprint .pprint (spot_tuner )
2342+ print (f"Error: { e } " )
23462343 raise e
2347- print (f"Experiment saved to { filename } " )
2344+ print (f"Experiment saved to { filename } " )
2345+
2346+ def remove_logger_handlers (self ) -> None :
2347+ """
2348+ Remove handlers from the logger to avoid pickling issues.
2349+ """
2350+ logger = logging .getLogger (__name__ )
2351+ for handler in logger .handlers [:]: # Copy the list to avoid modification during iteration
2352+ logger .removeHandler (handler )
2353+
2354+ def reattach_logger_handlers (self ) -> None :
2355+ """
2356+ Reattach handlers to the logger after unpickling.
2357+ """
2358+ logger = logging .getLogger (__name__ )
2359+ # configure the handler and formatter as needed
2360+ py_handler = logging .FileHandler (f"{ __name__ } .log" , mode = "w" )
2361+ py_formatter = logging .Formatter ("%(name)s %(asctime)s %(levelname)s %(message)s" )
2362+ # add formatter to the handler
2363+ py_handler .setFormatter (py_formatter )
2364+ # add handler to the logger
2365+ logger .addHandler (py_handler )
23482366
23492367 def init_spot_writer (self ) -> None :
23502368 """
0 commit comments