def test_steps_table(tmp_path): path = tmp_path / "test.db" database = load_database(path=path) make_steps_table(database) for status in ["scheduled", "running", "completed"]: append_row( { "status": status, "n_iterations": 0, "type": "optimization", "name": "bla", }, "steps", database, path, False, ) res, _ = read_new_rows(database, "steps", 1, "dict_of_lists") expected = { "rowid": [2, 3], "status": ["running", "completed"], "type": ["optimization", "optimization"], "name": ["bla", "bla"], "n_iterations": [0, 0], } assert res == expected
def test_read_optimization_iteration(tmp_path): path = tmp_path / "test.db" database = load_database(path=path) # add the optimization_iterations table make_optimization_iteration_table(database, first_eval={"output": 0.5}) iteration_data = [ {"external_params": np.array([0])}, {"external_params": np.array([1])}, {"external_params": np.array([2])}, ] for data in iteration_data: append_row(data, "optimization_iterations", database, path, False) # add the optimization_problem table make_optimization_problem_table(database) problem_data = {"params": pd.DataFrame(data=[10], columns=["value"])} append_row(problem_data, "optimization_problem", database, path, False) first_row_calc = read_optimization_iteration(path, 0) assert first_row_calc["rowid"] == 1 calculated_params = first_row_calc["params"] expected_params = pd.DataFrame(data=[0], columns=["value"]) assert_frame_equal(calculated_params, expected_params, check_dtype=False) last_row_calc = read_optimization_iteration(path, -1) assert last_row_calc["rowid"] == 3 calculated_params = last_row_calc["params"] expected_params = pd.DataFrame(data=[2], columns=["value"]) assert_frame_equal(calculated_params, expected_params, check_dtype=False)
def test_optimization_status_table(tmp_path): path = tmp_path / "test.db" database = load_database(path=path) make_optimization_status_table(database) for status in ["scheduled", "running", "success"]: append_row({"status": status}, "optimization_status", database, path, False) res, _ = read_new_rows(database, "optimization_status", 1, "dict_of_lists") expected = {"rowid": [2, 3], "status": ["running", "success"]} assert res == expected
def _log_final_status(res, database, path, log_options): if isinstance(res["success"], str) and res["success"].startswith("Not reported"): status = "unknown" elif res["success"]: status = "finished successfully" else: status = "failed" fast_logging = log_options.get("fast_logging", False) append_row({"status": status}, "optimization_status", database, path, fast_logging)
def _create_and_initialize_database(logging, log_options, first_eval, problem_data): # extract information path = Path(logging) fast_logging = log_options.get("fast_logging", False) if_table_exists = log_options.get("if_table_exists", "extend") if_database_exists = log_options.get("if_database_exists", "extend") if "if_exists" in log_options and "if_table_exists" not in log_options: warnings.warn( "The log_option 'if_exists' was renamed to 'if_table_exists'.") if logging.exists(): if if_database_exists == "raise": raise FileExistsError( f"The database {logging} already exists and the log_option " "'if_database_exists' is set to 'raise'") elif if_database_exists == "replace": logging.unlink() database = load_database(path=path, fast_logging=fast_logging) # create the optimization_iterations table make_optimization_iteration_table( database=database, first_eval=first_eval, if_exists=if_table_exists, ) # create and initialize the steps table; This is alway extended if it exists. make_steps_table(database, if_exists=if_table_exists) # create_and_initialize the optimization_problem table make_optimization_problem_table(database, if_exists=if_table_exists) not_saved = [ "criterion", "criterion_kwargs", "constraints", "derivative", "derivative_kwargs", "criterion_and_derivative", "criterion_and_derivative_kwargs", ] problem_data = { key: val for key, val in problem_data.items() if key not in not_saved } append_row(problem_data, "optimization_problem", database, path, fast_logging) return database
def test_optimization_iteration_table_scalar(tmp_path, iteration_data): path = tmp_path / "test.db" database = load_database(path=path) make_optimization_iteration_table(database, first_eval={"output": 0.5}) append_row(iteration_data, "optimization_iterations", database, path, False) res = read_last_rows(database, "optimization_iterations", 1, "list_of_dicts") assert isinstance(res, list) and isinstance(res[0], dict) res = res[0] assert res["rowid"] == 1 assert_array_equal(res["params"], iteration_data["params"]) for key in ["value", "timestamp"]: assert res[key] == iteration_data[key]
def test_optimization_problem_table(tmp_path, problem_data): path = tmp_path / "test.db" database = load_database(path=path) make_optimization_problem_table(database) append_row(problem_data, "optimization_problem", database, path, False) res = read_last_rows(database, "optimization_problem", 1, "list_of_dicts")[0] assert res["rowid"] == 1 for key, expected in problem_data.items(): if key == "criterion": assert res[key](np.ones(3)) == 3 elif isinstance(expected, np.ndarray): assert_array_equal(res[key], expected) else: assert res[key] == expected
def _log_new_evaluations( new_criterion, new_derivative, x, external_x, caught_exceptions, x_hash, database, database_path, log_options, ): """Write the new evaluations and additional information into the database. Note: There are some seemingly unnecessary type conversions because sqlalchemy can fail silently when called with numpy dtypes instead of the equivalent python types. """ data = { "external_params": external_x, "internal_params": x, "timestamp": datetime.datetime.now(), "distance_origin": float(np.linalg.norm(x)), "distance_ones": float(np.linalg.norm(x - 1)), "valid": True, } if new_derivative is not None: data["internal_derivative"] = new_derivative if caught_exceptions: separator = "\n" + "=" * 80 + "\n" data["exceptions"] = separator.join(caught_exceptions) data["valid"] = False if new_criterion is not None: data = {**data, **new_criterion} data["value"] = float(data["value"]) if "suffix" in log_options: name = "optimization_iterations" + "_" + log_options["suffix"] else: name = "optimization_iterations" fast_logging = log_options.get("fast_logging", False) append_row(data, name, database, database_path, fast_logging)
def test_read_table(tmp_path, iteration_data): path = tmp_path / "test.db" database = load_database(path=path) make_optimization_iteration_table(database, first_eval={"output": 0.5}) for i in range(1, 11): # sqlalchemy starts counting at 1 iteration_data["value"] = i iteration_data["step"] = i % 2 append_row(iteration_data, "optimization_iterations", database, path, False) table = read_table( database=database, table_name="optimization_iterations", return_type="dict_of_lists", ) assert table["rowid"] == list(range(1, 11)) assert table["step"] == [1, 0] * 5
def test_read_last_rows_stride(tmp_path, iteration_data): path = tmp_path / "test.db" database = load_database(path=path) make_optimization_iteration_table(database, first_eval={"output": 0.5}) for i in range(1, 11): # sqlalchemy starts counting at 1 iteration_data["value"] = i append_row(iteration_data, "optimization_iterations", database, path, False) res = read_last_rows( database=database, table_name="optimization_iterations", n_rows=3, return_type="dict_of_lists", stride=2, )["value"] expected = [6.0, 8.0, 10.0] assert res == expected
def test_read_last_rows_with_step(tmp_path, iteration_data): path = tmp_path / "test.db" database = load_database(path=path) make_optimization_iteration_table(database, first_eval={"output": 0.5}) for i in range(1, 11): # sqlalchemy starts counting at 1 iteration_data["value"] = i iteration_data["step"] = i % 2 append_row(iteration_data, "optimization_iterations", database, path, False) res = read_last_rows( database=database, table_name="optimization_iterations", n_rows=20, return_type="dict_of_lists", step=0, ) expected = [2, 4, 6, 8, 10] assert res["rowid"] == expected
def test_update_row(tmp_path, iteration_data): path = tmp_path / "test.db" database = load_database(path=path) make_optimization_iteration_table(database, first_eval={"output": 0.5}) for i in range(1, 11): # sqlalchemy starts counting at 1 iteration_data["value"] = i append_row(iteration_data, "optimization_iterations", database, path, False) update_row({"value": 20}, 8, "optimization_iterations", database, path, False) res = read_new_rows( database=database, table_name="optimization_iterations", last_retrieved=3, return_type="dict_of_lists", )[0]["value"] expected = [4, 5, 6, 7, 20, 9, 10] assert res == expected
def _create_and_initialize_database(logging, log_options, first_eval, problem_data): # extract information path = logging fast_logging = log_options.get("fast_logging", False) if_exists = log_options.get("if_exists", "extend") save_all_arguments = log_options.get("save_all_arguments", False) database = load_database(path=path, fast_logging=fast_logging) # create the optimization_iterations table make_optimization_iteration_table( database=database, first_eval=first_eval, if_exists=if_exists, ) # create and initialize the optimization_status table make_optimization_status_table(database, if_exists) append_row({"status": "running"}, "optimization_status", database, path, fast_logging) # create_and_initialize the optimization_problem table make_optimization_problem_table(database, if_exists, save_all_arguments) if not save_all_arguments: not_saved = [ "criterion", "criterion_kwargs", "constraints", "derivative", "derivative_kwargs", "criterion_and_derivative", "criterion_and_derivative_kwargs", ] problem_data = { key: val for key, val in problem_data.items() if key not in not_saved } append_row(problem_data, "optimization_problem", database, path, fast_logging) return database
def _log_new_evaluations( new_criterion, new_derivative, external_x, caught_exceptions, db_kwargs, fixed_log_data, ): """Write the new evaluations and additional information into the database. Note: There are some seemingly unnecessary type conversions because sqlalchemy can fail silently when called with numpy dtypes instead of the equivalent python types. """ data = { "params": external_x, "timestamp": datetime.datetime.now(), "valid": True, **fixed_log_data, } if new_derivative is not None: data["internal_derivative"] = new_derivative if caught_exceptions: separator = "\n" + "=" * 80 + "\n" data["exceptions"] = separator.join(caught_exceptions) data["valid"] = False if new_criterion is not None: data = {**data, **new_criterion} data["value"] = float(data["value"]) name = "optimization_iterations" append_row(data, name, **db_kwargs)