6
from subprocess import CalledProcessError
9
from typing import Callable
10
from typing import Optional
11
from typing import Tuple
12
from unittest.mock import MagicMock
13
from unittest.mock import patch
17
from pandas import Timedelta
18
from pandas import Timestamp
24
from optuna.exceptions import CLIUsageError
25
from optuna.exceptions import ExperimentalWarning
26
from optuna.storages import JournalFileStorage
27
from optuna.storages import JournalRedisStorage
28
from optuna.storages import JournalStorage
29
from optuna.storages import RDBStorage
30
from optuna.storages._base import DEFAULT_STUDY_NAME_PREFIX
31
from optuna.study import StudyDirection
32
from optuna.testing.storages import StorageSupplier
33
from optuna.testing.tempfile_pool import NamedTemporaryFilePool
34
from optuna.trial import Trial
35
from optuna.trial import TrialState
39
def objective_func(trial: Trial) -> float:
40
x = trial.suggest_float("x", -10, 10)
45
def objective_func_branched_search_space(trial: Trial) -> float:
46
c = trial.suggest_categorical("c", ("A", "B"))
48
x = trial.suggest_float("x", -10, 10)
51
y = trial.suggest_float("y", -10, 10)
56
def objective_func_multi_objective(trial: Trial) -> Tuple[float, float]:
57
x = trial.suggest_float("x", -10, 10)
58
return (x + 5) ** 2, (x - 5) ** 2
61
def _parse_output(output: str, output_format: str) -> Any:
66
The output of command.
68
The format of output specified by command.
71
For table format, a list of dict formatted rows.
72
For JSON or YAML format, a list or a dict corresponding to ``output``.
74
if output_format == "value":
77
return [{"name": values} for values in output.split(os.linesep)]
78
elif output_format == "table":
79
rows = output.split(os.linesep)
80
assert all(len(rows[0]) == len(row) for row in rows)
82
assert rows[0] == rows[2] == rows[-1]
84
keys = [r.strip() for r in rows[1].split("|")[1:-1]]
86
for record in rows[3:-1]:
88
for key, attr in zip(keys, record.split("|")[1:-1]):
89
attrs[key] = attr.strip()
92
elif output_format == "json":
93
return json.loads(output)
94
elif output_format == "yaml":
95
return yaml.safe_load(output)
100
@pytest.mark.skip_coverage
101
def test_create_study_command() -> None:
102
with StorageSupplier("sqlite") as storage:
103
assert isinstance(storage, RDBStorage)
104
storage_url = str(storage.engine.url)
107
command = ["optuna", "create-study", "--storage", storage_url]
108
subprocess.check_call(command)
111
study_name = str(subprocess.check_output(command).decode().strip())
112
name_re = r"^no-name-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$"
113
assert re.match(name_re, study_name) is not None
116
study_id = storage.get_study_id_from_name(study_name)
120
@pytest.mark.skip_coverage
121
def test_create_study_command_with_study_name() -> None:
122
with StorageSupplier("sqlite") as storage:
123
assert isinstance(storage, RDBStorage)
124
storage_url = str(storage.engine.url)
125
study_name = "test_study"
128
command = ["optuna", "create-study", "--storage", storage_url, "--study-name", study_name]
129
study_name = str(subprocess.check_output(command).decode().strip())
132
study_id = storage.get_study_id_from_name(study_name)
133
assert storage.get_study_name_from_id(study_id) == study_name
136
@pytest.mark.skip_coverage
137
def test_create_study_command_without_storage_url() -> None:
138
with pytest.raises(subprocess.CalledProcessError) as err:
139
subprocess.check_output(
140
["optuna", "create-study"],
141
env={k: v for k, v in os.environ.items() if k != "OPTUNA_STORAGE"},
143
usage = err.value.output.decode()
144
assert usage.startswith("usage:")
147
@pytest.mark.skip_coverage
148
def test_create_study_command_with_storage_env() -> None:
149
with StorageSupplier("sqlite") as storage:
150
assert isinstance(storage, RDBStorage)
151
storage_url = str(storage.engine.url)
154
command = ["optuna", "create-study"]
155
env = {**os.environ, "OPTUNA_STORAGE": storage_url}
156
subprocess.check_call(command, env=env)
159
study_name = str(subprocess.check_output(command, env=env).decode().strip())
160
name_re = r"^no-name-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$"
161
assert re.match(name_re, study_name) is not None
164
study_id = storage.get_study_id_from_name(study_name)
168
@pytest.mark.skip_coverage
169
def test_create_study_command_with_direction() -> None:
170
with StorageSupplier("sqlite") as storage:
171
assert isinstance(storage, RDBStorage)
172
storage_url = str(storage.engine.url)
174
command = ["optuna", "create-study", "--storage", storage_url, "--direction", "minimize"]
175
study_name = str(subprocess.check_output(command).decode().strip())
176
study_id = storage.get_study_id_from_name(study_name)
177
assert storage.get_study_directions(study_id) == [StudyDirection.MINIMIZE]
179
command = ["optuna", "create-study", "--storage", storage_url, "--direction", "maximize"]
180
study_name = str(subprocess.check_output(command).decode().strip())
181
study_id = storage.get_study_id_from_name(study_name)
182
assert storage.get_study_directions(study_id) == [StudyDirection.MAXIMIZE]
184
command = ["optuna", "create-study", "--storage", storage_url, "--direction", "test"]
187
with pytest.raises(subprocess.CalledProcessError):
188
subprocess.check_call(command)
191
@pytest.mark.skip_coverage
192
def test_create_study_command_with_multiple_directions() -> None:
193
with StorageSupplier("sqlite") as storage:
194
assert isinstance(storage, RDBStorage)
195
storage_url = str(storage.engine.url)
206
study_name = str(subprocess.check_output(command).decode().strip())
207
study_id = storage.get_study_id_from_name(study_name)
208
expected_directions = [StudyDirection.MINIMIZE, StudyDirection.MAXIMIZE]
209
assert storage.get_study_directions(study_id) == expected_directions
223
with pytest.raises(subprocess.CalledProcessError):
224
subprocess.check_call(command)
240
with pytest.raises(subprocess.CalledProcessError):
241
subprocess.check_call(command)
244
@pytest.mark.skip_coverage
245
def test_delete_study_command() -> None:
246
with StorageSupplier("sqlite") as storage:
247
assert isinstance(storage, RDBStorage)
248
storage_url = str(storage.engine.url)
249
study_name = "delete-study-test"
252
command = ["optuna", "create-study", "--storage", storage_url, "--study-name", study_name]
253
subprocess.check_call(command)
254
assert study_name in {s.study_name: s for s in storage.get_all_studies()}
257
command = ["optuna", "delete-study", "--storage", storage_url, "--study-name", study_name]
258
subprocess.check_call(command)
259
assert study_name not in {s.study_name: s for s in storage.get_all_studies()}
262
@pytest.mark.skip_coverage
263
def test_delete_study_command_without_storage_url() -> None:
264
with pytest.raises(subprocess.CalledProcessError):
265
subprocess.check_output(
266
["optuna", "delete-study", "--study-name", "dummy_study"],
267
env={k: v for k, v in os.environ.items() if k != "OPTUNA_STORAGE"},
271
@pytest.mark.skip_coverage
272
def test_study_set_user_attr_command() -> None:
273
with StorageSupplier("sqlite") as storage:
274
assert isinstance(storage, RDBStorage)
275
storage_url = str(storage.engine.url)
278
study_name = storage.get_study_name_from_id(
279
storage.create_new_study(directions=[StudyDirection.MINIMIZE])
292
example_attrs = {"architecture": "ResNet", "baselen_score": "0.002"}
293
for key, value in example_attrs.items():
294
subprocess.check_call(base_command + ["--key", key, "--value", value])
297
study_id = storage.get_study_id_from_name(study_name)
298
study_user_attrs = storage.get_study_user_attrs(study_id)
299
assert len(study_user_attrs) == 2
300
assert all(study_user_attrs[k] == v for k, v in example_attrs.items())
303
@pytest.mark.skip_coverage
304
@pytest.mark.parametrize("output_format", (None, "table", "json", "yaml"))
305
def test_study_names_command(output_format: Optional[str]) -> None:
306
with StorageSupplier("sqlite") as storage:
307
assert isinstance(storage, RDBStorage)
308
storage_url = str(storage.engine.url)
310
expected_study_names = ["study-names-test1", "study-names-test2"]
311
expected_column_name = "name"
320
expected_study_names[0],
322
subprocess.check_output(command)
325
command = ["optuna", "study-names", "--storage", storage_url]
326
if output_format is not None:
327
command += ["--format", output_format]
328
output = str(subprocess.check_output(command).decode().strip())
329
study_names = _parse_output(output, output_format or "value")
332
assert len(study_names) == 1
333
assert study_names[0]["name"] == expected_study_names[0]
342
expected_study_names[1],
344
subprocess.check_output(command)
347
command = ["optuna", "study-names", "--storage", storage_url]
348
if output_format is not None:
349
command += ["--format", output_format]
350
output = str(subprocess.check_output(command).decode().strip())
351
study_names = _parse_output(output, output_format or "value")
353
assert len(study_names) == 2
354
for i, study_name in enumerate(study_names):
355
assert list(study_name.keys()) == [expected_column_name]
356
assert study_name["name"] == expected_study_names[i]
359
@pytest.mark.skip_coverage
360
def test_study_names_command_without_storage_url() -> None:
361
with pytest.raises(subprocess.CalledProcessError):
362
subprocess.check_output(
363
["optuna", "study-names", "--study-name", "dummy_study"],
364
env={k: v for k, v in os.environ.items() if k != "OPTUNA_STORAGE"},
368
@pytest.mark.skip_coverage
369
@pytest.mark.parametrize("output_format", (None, "table", "json", "yaml"))
370
def test_studies_command(output_format: Optional[str]) -> None:
371
with StorageSupplier("sqlite") as storage:
372
assert isinstance(storage, RDBStorage)
373
storage_url = str(storage.engine.url)
376
study_1 = optuna.create_study(storage=storage)
379
command = ["optuna", "studies", "--storage", storage_url]
380
if output_format is not None:
381
command += ["--format", output_format]
383
output = str(subprocess.check_output(command).decode().strip())
384
studies = _parse_output(output, output_format or "table")
386
expected_keys = ["name", "direction", "n_trials", "datetime_start"]
389
if output_format is None or output_format == "table":
390
assert list(studies[0].keys()) == expected_keys
392
assert set(studies[0].keys()) == set(expected_keys)
395
study_2 = optuna.create_study(
396
storage=storage, study_name="study_2", directions=["minimize", "maximize"]
398
study_2.optimize(objective_func_multi_objective, n_trials=10)
399
study_2.set_user_attr("key_1", "value_1")
400
study_2.set_user_attr("key_2", "value_2")
403
output = str(subprocess.check_output(command).decode().strip())
404
studies = _parse_output(output, output_format or "table")
406
expected_keys = ["name", "direction", "n_trials", "datetime_start", "user_attrs"]
408
assert len(studies) == 2
409
for study in studies:
410
if output_format is None or output_format == "table":
411
assert list(study.keys()) == expected_keys
413
assert set(study.keys()) == set(expected_keys)
416
assert studies[0]["name"] == study_1.study_name
417
if output_format is None or output_format == "table":
418
assert studies[0]["n_trials"] == "0"
419
assert eval(studies[0]["direction"]) == ("MINIMIZE",)
420
assert eval(studies[0]["user_attrs"]) == {}
422
assert studies[0]["n_trials"] == 0
423
assert studies[0]["direction"] == ["MINIMIZE"]
424
assert studies[0]["user_attrs"] == {}
427
assert studies[1]["name"] == study_2.study_name
428
if output_format is None or output_format == "table":
429
assert studies[1]["n_trials"] == "10"
430
assert eval(studies[1]["direction"]) == ("MINIMIZE", "MAXIMIZE")
431
assert eval(studies[1]["user_attrs"]) == {"key_1": "value_1", "key_2": "value_2"}
433
assert studies[1]["n_trials"] == 10
434
assert studies[1]["direction"] == ["MINIMIZE", "MAXIMIZE"]
435
assert studies[1]["user_attrs"] == {"key_1": "value_1", "key_2": "value_2"}
438
@pytest.mark.skip_coverage
439
@pytest.mark.parametrize("output_format", (None, "table", "json", "yaml"))
440
def test_studies_command_flatten(output_format: Optional[str]) -> None:
441
with StorageSupplier("sqlite") as storage:
442
assert isinstance(storage, RDBStorage)
443
storage_url = str(storage.engine.url)
446
study_1 = optuna.create_study(storage=storage)
449
command = ["optuna", "studies", "--storage", storage_url, "--flatten"]
450
if output_format is not None:
451
command += ["--format", output_format]
453
output = str(subprocess.check_output(command).decode().strip())
454
studies = _parse_output(output, output_format or "table")
464
if output_format is None or output_format == "table":
465
assert list(studies[0].keys()) == expected_keys_1
467
assert set(studies[0].keys()) == set(expected_keys_1)
470
study_2 = optuna.create_study(
471
storage=storage, study_name="study_2", directions=["minimize", "maximize"]
473
study_2.optimize(objective_func_multi_objective, n_trials=10)
474
study_2.set_user_attr("key_1", "value_1")
475
study_2.set_user_attr("key_2", "value_2")
478
output = str(subprocess.check_output(command).decode().strip())
479
studies = _parse_output(output, output_format or "table")
481
if output_format is None or output_format == "table":
482
expected_keys_1 = expected_keys_2 = [
491
expected_keys_1 = ["name", "direction_0", "n_trials", "datetime_start", "user_attrs"]
501
assert len(studies) == 2
502
if output_format is None or output_format == "table":
503
assert list(studies[0].keys()) == expected_keys_1
504
assert list(studies[1].keys()) == expected_keys_2
506
assert set(studies[0].keys()) == set(expected_keys_1)
507
assert set(studies[1].keys()) == set(expected_keys_2)
510
assert studies[0]["name"] == study_1.study_name
511
if output_format is None or output_format == "table":
512
assert studies[0]["n_trials"] == "0"
513
assert studies[0]["user_attrs"] == "{}"
515
assert studies[0]["n_trials"] == 0
516
assert studies[0]["user_attrs"] == {}
517
assert studies[0]["direction_0"] == "MINIMIZE"
520
assert studies[1]["name"] == study_2.study_name
521
if output_format is None or output_format == "table":
522
assert studies[1]["n_trials"] == "10"
523
assert studies[1]["user_attrs"] == "{'key_1': 'value_1', 'key_2': 'value_2'}"
525
assert studies[1]["n_trials"] == 10
526
assert studies[1]["user_attrs"] == {"key_1": "value_1", "key_2": "value_2"}
527
assert studies[1]["direction_0"] == "MINIMIZE"
528
assert studies[1]["direction_1"] == "MAXIMIZE"
531
@pytest.mark.skip_coverage
532
@pytest.mark.parametrize("objective", (objective_func, objective_func_branched_search_space))
533
@pytest.mark.parametrize("output_format", (None, "table", "json", "yaml"))
534
def test_trials_command(objective: Callable[[Trial], float], output_format: Optional[str]) -> None:
535
with StorageSupplier("sqlite") as storage:
536
assert isinstance(storage, RDBStorage)
537
storage_url = str(storage.engine.url)
538
study_name = "test_study"
541
study = optuna.create_study(storage=storage, study_name=study_name)
542
study.optimize(objective, n_trials=n_trials)
564
if output_format is not None:
565
command += ["--format", output_format]
567
output = str(subprocess.check_output(command).decode().strip())
568
trials = _parse_output(output, output_format or "table")
570
assert len(trials) == n_trials
572
df = study.trials_dataframe(attrs, multi_index=True)
574
for i, trial in enumerate(trials):
575
for key in df.columns:
576
expected_value = df.loc[i][key]
581
and isinstance(expected_value, float)
582
and np.isnan(expected_value)
584
if output_format is None or output_format == "table":
585
assert key[1] not in eval(trial["params"])
587
assert key[1] not in trial["params"]
591
value = trial[key[0]]
593
if output_format is None or output_format == "table":
594
value = eval(trial[key[0]])[key[1]]
596
value = trial[key[0]][key[1]]
598
if isinstance(value, (int, float)):
599
if np.isnan(expected_value):
600
assert np.isnan(value)
602
assert value == expected_value
603
elif isinstance(expected_value, Timestamp):
604
assert value == expected_value.strftime("%Y-%m-%d %H:%M:%S")
605
elif isinstance(expected_value, Timedelta):
606
assert value == str(expected_value.to_pytimedelta())
608
assert value == str(expected_value)
611
@pytest.mark.skip_coverage
612
@pytest.mark.parametrize("objective", (objective_func, objective_func_branched_search_space))
613
@pytest.mark.parametrize("output_format", (None, "table", "json", "yaml"))
614
def test_trials_command_flatten(
615
objective: Callable[[Trial], float], output_format: Optional[str]
617
with StorageSupplier("sqlite") as storage:
618
assert isinstance(storage, RDBStorage)
619
storage_url = str(storage.engine.url)
620
study_name = "test_study"
623
study = optuna.create_study(storage=storage, study_name=study_name)
624
study.optimize(objective, n_trials=n_trials)
647
if output_format is not None:
648
command += ["--format", output_format]
650
output = str(subprocess.check_output(command).decode().strip())
651
trials = _parse_output(output, output_format or "table")
653
assert len(trials) == n_trials
655
df = study.trials_dataframe(attrs)
657
for i, trial in enumerate(trials):
658
assert set(trial.keys()) <= set(df.columns)
659
for key in df.columns:
660
expected_value = df.loc[i][key]
664
key.startswith("params_")
665
and isinstance(expected_value, float)
666
and np.isnan(expected_value)
668
if output_format is None or output_format == "table":
669
assert trial[key] == ""
671
assert key not in trial
676
if isinstance(value, (int, float)):
677
if np.isnan(expected_value):
678
assert np.isnan(value)
680
assert value == expected_value
681
elif isinstance(expected_value, Timestamp):
682
assert value == expected_value.strftime("%Y-%m-%d %H:%M:%S")
683
elif isinstance(expected_value, Timedelta):
684
assert value == str(expected_value.to_pytimedelta())
686
assert value == str(expected_value)
689
@pytest.mark.skip_coverage
690
@pytest.mark.parametrize("objective", (objective_func, objective_func_branched_search_space))
691
@pytest.mark.parametrize("output_format", (None, "table", "json", "yaml"))
692
def test_best_trial_command(
693
objective: Callable[[Trial], float], output_format: Optional[str]
695
with StorageSupplier("sqlite") as storage:
696
assert isinstance(storage, RDBStorage)
697
storage_url = str(storage.engine.url)
698
study_name = "test_study"
701
study = optuna.create_study(storage=storage, study_name=study_name)
702
study.optimize(objective, n_trials=n_trials)
724
if output_format is not None:
725
command += ["--format", output_format]
727
output = str(subprocess.check_output(command).decode().strip())
728
best_trial = _parse_output(output, output_format or "table")
730
if output_format is None or output_format == "table":
731
assert len(best_trial) == 1
732
best_trial = best_trial[0]
734
df = study.trials_dataframe(attrs, multi_index=True)
736
for key in df.columns:
737
expected_value = df.loc[study.best_trial.number][key]
742
and isinstance(expected_value, float)
743
and np.isnan(expected_value)
745
if output_format is None or output_format == "table":
746
assert key[1] not in eval(best_trial["params"])
748
assert key[1] not in best_trial["params"]
752
value = best_trial[key[0]]
754
if output_format is None or output_format == "table":
755
value = eval(best_trial[key[0]])[key[1]]
757
value = best_trial[key[0]][key[1]]
759
if isinstance(value, (int, float)):
760
if np.isnan(expected_value):
761
assert np.isnan(value)
763
assert value == expected_value
764
elif isinstance(expected_value, Timestamp):
765
assert value == expected_value.strftime("%Y-%m-%d %H:%M:%S")
766
elif isinstance(expected_value, Timedelta):
767
assert value == str(expected_value.to_pytimedelta())
769
assert value == str(expected_value)
772
@pytest.mark.skip_coverage
773
@pytest.mark.parametrize("objective", (objective_func, objective_func_branched_search_space))
774
@pytest.mark.parametrize("output_format", (None, "table", "json", "yaml"))
775
def test_best_trial_command_flatten(
776
objective: Callable[[Trial], float], output_format: Optional[str]
778
with StorageSupplier("sqlite") as storage:
779
assert isinstance(storage, RDBStorage)
780
storage_url = str(storage.engine.url)
781
study_name = "test_study"
784
study = optuna.create_study(storage=storage, study_name=study_name)
785
study.optimize(objective, n_trials=n_trials)
808
if output_format is not None:
809
command += ["--format", output_format]
811
output = str(subprocess.check_output(command).decode().strip())
812
best_trial = _parse_output(output, output_format or "table")
814
if output_format is None or output_format == "table":
815
assert len(best_trial) == 1
816
best_trial = best_trial[0]
818
df = study.trials_dataframe(attrs)
820
assert set(best_trial.keys()) <= set(df.columns)
821
for key in df.columns:
822
expected_value = df.loc[study.best_trial.number][key]
826
key.startswith("params_")
827
and isinstance(expected_value, float)
828
and np.isnan(expected_value)
830
if output_format is None or output_format == "table":
831
assert best_trial[key] == ""
833
assert key not in best_trial
836
value = best_trial[key]
837
if isinstance(value, (int, float)):
838
if np.isnan(expected_value):
839
assert np.isnan(value)
841
assert value == expected_value
842
elif isinstance(expected_value, Timestamp):
843
assert value == expected_value.strftime("%Y-%m-%d %H:%M:%S")
844
elif isinstance(expected_value, Timedelta):
845
assert value == str(expected_value.to_pytimedelta())
847
assert value == str(expected_value)
850
@pytest.mark.skip_coverage
851
@pytest.mark.parametrize("output_format", (None, "table", "json", "yaml"))
852
def test_best_trials_command(output_format: Optional[str]) -> None:
853
with StorageSupplier("sqlite") as storage:
854
assert isinstance(storage, RDBStorage)
855
storage_url = str(storage.engine.url)
856
study_name = "test_study"
859
study = optuna.create_study(
860
storage=storage, study_name=study_name, directions=("minimize", "minimize")
862
study.optimize(objective_func_multi_objective, n_trials=n_trials)
884
if output_format is not None:
885
command += ["--format", output_format]
887
output = str(subprocess.check_output(command).decode().strip())
888
trials = _parse_output(output, output_format or "table")
889
best_trials = [trial.number for trial in study.best_trials]
891
assert len(trials) == len(best_trials)
893
df = study.trials_dataframe(attrs, multi_index=True)
896
number = int(trial["number"]) if output_format in (None, "table") else trial["number"]
897
assert number in best_trials
898
for key in df.columns:
899
expected_value = df.loc[number][key]
904
and isinstance(expected_value, float)
905
and np.isnan(expected_value)
907
if output_format is None or output_format == "table":
908
assert key[1] not in eval(trial["params"])
910
assert key[1] not in trial["params"]
914
value = trial[key[0]]
916
if output_format is None or output_format == "table":
917
value = eval(trial[key[0]])[key[1]]
919
value = trial[key[0]][key[1]]
921
if isinstance(value, (int, float)):
922
if np.isnan(expected_value):
923
assert np.isnan(value)
925
assert value == expected_value
926
elif isinstance(expected_value, Timestamp):
927
assert value == expected_value.strftime("%Y-%m-%d %H:%M:%S")
928
elif isinstance(expected_value, Timedelta):
929
assert value == str(expected_value.to_pytimedelta())
931
assert value == str(expected_value)
934
@pytest.mark.skip_coverage
935
@pytest.mark.parametrize("output_format", (None, "table", "json", "yaml"))
936
def test_best_trials_command_flatten(output_format: Optional[str]) -> None:
937
with StorageSupplier("sqlite") as storage:
938
assert isinstance(storage, RDBStorage)
939
storage_url = str(storage.engine.url)
940
study_name = "test_study"
943
study = optuna.create_study(
944
storage=storage, study_name=study_name, directions=("minimize", "minimize")
946
study.optimize(objective_func_multi_objective, n_trials=n_trials)
969
if output_format is not None:
970
command += ["--format", output_format]
972
output = str(subprocess.check_output(command).decode().strip())
973
trials = _parse_output(output, output_format or "table")
974
best_trials = [trial.number for trial in study.best_trials]
976
assert len(trials) == len(best_trials)
978
df = study.trials_dataframe(attrs)
981
assert set(trial.keys()) <= set(df.columns)
982
number = int(trial["number"]) if output_format in (None, "table") else trial["number"]
983
for key in df.columns:
984
expected_value = df.loc[number][key]
988
key.startswith("params_")
989
and isinstance(expected_value, float)
990
and np.isnan(expected_value)
992
if output_format is None or output_format == "table":
993
assert trial[key] == ""
995
assert key not in trial
999
if isinstance(value, (int, float)):
1000
if np.isnan(expected_value):
1001
assert np.isnan(value)
1003
assert value == expected_value
1004
elif isinstance(expected_value, Timestamp):
1005
assert value == expected_value.strftime("%Y-%m-%d %H:%M:%S")
1006
elif isinstance(expected_value, Timedelta):
1007
assert value == str(expected_value.to_pytimedelta())
1009
assert value == str(expected_value)
1012
@pytest.mark.skip_coverage
1013
def test_create_study_command_with_skip_if_exists() -> None:
1014
with StorageSupplier("sqlite") as storage:
1015
assert isinstance(storage, RDBStorage)
1016
storage_url = str(storage.engine.url)
1017
study_name = "test_study"
1020
command = ["optuna", "create-study", "--storage", storage_url, "--study-name", study_name]
1021
study_name = str(subprocess.check_output(command).decode().strip())
1024
study_id = storage.get_study_id_from_name(study_name)
1025
assert storage.get_study_name_from_id(study_id) == study_name
1028
command = ["optuna", "create-study", "--storage", storage_url, "--study-name", study_name]
1029
with pytest.raises(subprocess.CalledProcessError):
1030
subprocess.check_output(command)
1042
study_name = str(subprocess.check_output(command).decode().strip())
1043
new_study_id = storage.get_study_id_from_name(study_name)
1044
assert study_id == new_study_id
1047
@pytest.mark.skip_coverage
1048
def test_study_optimize_command() -> None:
1049
with StorageSupplier("sqlite") as storage:
1050
assert isinstance(storage, RDBStorage)
1051
storage_url = str(storage.engine.url)
1053
study_name = storage.get_study_name_from_id(
1054
storage.create_new_study(directions=[StudyDirection.MINIMIZE])
1069
subprocess.check_call(command)
1071
study = optuna.load_study(storage=storage_url, study_name=study_name)
1072
assert len(study.trials) == 10
1073
assert "x" in study.best_params
1076
assert storage.get_study_name_from_id(study._study_id).startswith(
1077
DEFAULT_STUDY_NAME_PREFIX
1081
@pytest.mark.skip_coverage
1082
def test_study_optimize_command_inconsistent_args() -> None:
1083
with NamedTemporaryFilePool() as tf:
1084
db_url = "sqlite:///{}".format(tf.name)
1087
with pytest.raises(subprocess.CalledProcessError):
1088
subprocess.check_call(
1103
@pytest.mark.skip_coverage
1104
def test_empty_argv() -> None:
1105
command_empty = ["optuna"]
1106
command_empty_output = str(subprocess.check_output(command_empty))
1108
command_help = ["optuna", "help"]
1109
command_help_output = str(subprocess.check_output(command_help))
1111
assert command_empty_output == command_help_output
1114
def test_check_storage_url() -> None:
1115
storage_in_args = "sqlite:///args.db"
1116
assert storage_in_args == optuna.cli._check_storage_url(storage_in_args)
1118
with pytest.warns(ExperimentalWarning):
1119
with patch.dict("optuna.cli.os.environ", {"OPTUNA_STORAGE": "sqlite:///args.db"}):
1120
optuna.cli._check_storage_url(None)
1122
with pytest.raises(CLIUsageError):
1123
optuna.cli._check_storage_url(None)
1126
@pytest.mark.skipif(platform.system() == "Windows", reason="Skip on Windows")
1127
@patch("optuna.storages._journal.redis.redis")
1128
def test_get_storage_without_storage_class(mock_redis: MagicMock) -> None:
1129
with tempfile.NamedTemporaryFile(suffix=".db") as fp:
1130
storage = optuna.cli._get_storage(f"sqlite:///{fp.name}", storage_class=None)
1131
assert isinstance(storage, RDBStorage)
1133
with tempfile.NamedTemporaryFile(suffix=".log") as fp:
1134
storage = optuna.cli._get_storage(fp.name, storage_class=None)
1135
assert isinstance(storage, JournalStorage)
1136
assert isinstance(storage._backend, JournalFileStorage)
1138
mock_redis.Redis = fakeredis.FakeRedis
1139
storage = optuna.cli._get_storage("redis://localhost:6379", storage_class=None)
1140
assert isinstance(storage, JournalStorage)
1141
assert isinstance(storage._backend, JournalRedisStorage)
1143
with pytest.raises(CLIUsageError):
1144
optuna.cli._get_storage("./file-not-found.log", storage_class=None)
1147
@pytest.mark.skipif(platform.system() == "Windows", reason="Skip on Windows")
1148
@patch("optuna.storages._journal.redis.redis")
1149
def test_get_storage_with_storage_class(mock_redis: MagicMock) -> None:
1150
with tempfile.NamedTemporaryFile(suffix=".db") as fp:
1151
storage = optuna.cli._get_storage(f"sqlite:///{fp.name}", storage_class=None)
1152
assert isinstance(storage, RDBStorage)
1154
with tempfile.NamedTemporaryFile(suffix=".log") as fp:
1155
storage = optuna.cli._get_storage(fp.name, storage_class="JournalFileStorage")
1156
assert isinstance(storage, JournalStorage)
1157
assert isinstance(storage._backend, JournalFileStorage)
1159
mock_redis.Redis = fakeredis.FakeRedis
1160
storage = optuna.cli._get_storage(
1161
"redis:///localhost:6379", storage_class="JournalRedisStorage"
1163
assert isinstance(storage, JournalStorage)
1164
assert isinstance(storage._backend, JournalRedisStorage)
1166
with pytest.raises(CLIUsageError):
1167
with tempfile.NamedTemporaryFile(suffix=".db") as fp:
1168
optuna.cli._get_storage(f"sqlite:///{fp.name}", storage_class="InMemoryStorage")
1171
@pytest.mark.skip_coverage
1172
def test_storage_upgrade_command() -> None:
1173
with StorageSupplier("sqlite") as storage:
1174
assert isinstance(storage, RDBStorage)
1175
storage_url = str(storage.engine.url)
1177
command = ["optuna", "storage", "upgrade"]
1178
with pytest.raises(CalledProcessError):
1179
subprocess.check_call(
1181
env={k: v for k, v in os.environ.items() if k != "OPTUNA_STORAGE"},
1184
command.extend(["--storage", storage_url])
1185
subprocess.check_call(command)
1188
@pytest.mark.skip_coverage
1189
def test_storage_upgrade_command_with_invalid_url() -> None:
1190
with StorageSupplier("sqlite") as storage:
1191
assert isinstance(storage, RDBStorage)
1193
command = ["optuna", "storage", "upgrade", "--storage", "invalid-storage-url"]
1194
with pytest.raises(CalledProcessError):
1195
subprocess.check_call(command)
1198
@pytest.mark.skip_coverage
1199
@pytest.mark.parametrize(
1200
"direction,directions,sampler,sampler_kwargs,output_format",
1202
(None, None, None, None, None),
1203
("minimize", None, None, None, None),
1204
(None, "minimize maximize", None, None, None),
1205
(None, None, "RandomSampler", None, None),
1206
(None, None, "TPESampler", '{"multivariate": true}', None),
1207
(None, None, None, None, "json"),
1208
(None, None, None, None, "yaml"),
1212
direction: Optional[str],
1213
directions: Optional[str],
1214
sampler: Optional[str],
1215
sampler_kwargs: Optional[str],
1216
output_format: Optional[str],
1218
study_name = "test_study"
1220
'{"x": {"name": "FloatDistribution", "attributes": {"low": 0.0, "high": 1.0}}, '
1221
'"y": {"name": "CategoricalDistribution", "attributes": {"choices": ["foo"]}}}'
1224
with NamedTemporaryFilePool() as tf:
1225
db_url = "sqlite:///{}".format(tf.name)
1238
if direction is not None:
1239
args += ["--direction", direction]
1240
if directions is not None:
1241
args += ["--directions"] + directions.split()
1242
if sampler is not None:
1243
args += ["--sampler", sampler]
1244
if sampler_kwargs is not None:
1245
args += ["--sampler-kwargs", sampler_kwargs]
1246
if output_format is not None:
1247
args += ["--format", output_format]
1249
result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1250
output = str(result.stdout.decode().strip())
1251
trial = _parse_output(output, output_format or "json")
1253
if output_format == "table":
1254
assert len(trial) == 1
1256
assert trial["number"] == "0"
1257
params = eval(trial["params"])
1258
assert len(params) == 2
1259
assert 0 <= params["x"] <= 1
1260
assert params["y"] == "foo"
1262
assert trial["number"] == 0
1263
assert 0 <= trial["params"]["x"] <= 1
1264
assert trial["params"]["y"] == "foo"
1266
if direction is not None or directions is not None:
1267
warning_message = result.stderr.decode()
1268
assert "FutureWarning" in warning_message
1271
@pytest.mark.skip_coverage
1272
@pytest.mark.parametrize(
1273
"direction,directions,sampler,sampler_kwargs,output_format",
1275
(None, None, None, None, None),
1276
("minimize", None, None, None, None),
1277
(None, "minimize maximize", None, None, None),
1278
(None, None, "RandomSampler", None, None),
1279
(None, None, "TPESampler", '{"multivariate": true}', None),
1280
(None, None, None, None, "json"),
1281
(None, None, None, None, "yaml"),
1284
def test_ask_flatten(
1285
direction: Optional[str],
1286
directions: Optional[str],
1287
sampler: Optional[str],
1288
sampler_kwargs: Optional[str],
1289
output_format: Optional[str],
1291
study_name = "test_study"
1293
'{"x": {"name": "FloatDistribution", "attributes": {"low": 0.0, "high": 1.0}}, '
1294
'"y": {"name": "CategoricalDistribution", "attributes": {"choices": ["foo"]}}}'
1297
with NamedTemporaryFilePool() as tf:
1298
db_url = "sqlite:///{}".format(tf.name)
1312
if direction is not None:
1313
args += ["--direction", direction]
1314
if directions is not None:
1315
args += ["--directions"] + directions.split()
1316
if sampler is not None:
1317
args += ["--sampler", sampler]
1318
if sampler_kwargs is not None:
1319
args += ["--sampler-kwargs", sampler_kwargs]
1320
if output_format is not None:
1321
args += ["--format", output_format]
1323
result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1324
output = str(result.stdout.decode().strip())
1325
trial = _parse_output(output, output_format or "json")
1327
if output_format == "table":
1328
assert len(trial) == 1
1330
assert trial["number"] == "0"
1331
assert 0 <= float(trial["params_x"]) <= 1
1332
assert trial["params_y"] == "foo"
1334
assert trial["number"] == 0
1335
assert 0 <= trial["params_x"] <= 1
1336
assert trial["params_y"] == "foo"
1338
if direction is not None or directions is not None:
1339
warning_message = result.stderr.decode()
1340
assert "FutureWarning" in warning_message
1343
@pytest.mark.skip_coverage
1344
@pytest.mark.parametrize("output_format", (None, "table", "json", "yaml"))
1345
def test_ask_empty_search_space(output_format: str) -> None:
1346
study_name = "test_study"
1348
with NamedTemporaryFilePool() as tf:
1349
db_url = "sqlite:///{}".format(tf.name)
1360
if output_format is not None:
1361
args += ["--format", output_format]
1363
output = str(subprocess.check_output(args).decode().strip())
1364
trial = _parse_output(output, output_format or "json")
1366
if output_format == "table":
1367
assert len(trial) == 1
1369
assert trial["number"] == "0"
1370
assert trial["params"] == "{}"
1372
assert trial["number"] == 0
1373
assert trial["params"] == {}
1376
@pytest.mark.skip_coverage
1377
@pytest.mark.parametrize("output_format", (None, "table", "json", "yaml"))
1378
def test_ask_empty_search_space_flatten(output_format: str) -> None:
1379
study_name = "test_study"
1381
with NamedTemporaryFilePool() as tf:
1382
db_url = "sqlite:///{}".format(tf.name)
1394
if output_format is not None:
1395
args += ["--format", output_format]
1397
output = str(subprocess.check_output(args).decode().strip())
1398
trial = _parse_output(output, output_format or "json")
1400
if output_format == "table":
1401
assert len(trial) == 1
1403
assert trial["number"] == "0"
1404
assert "params" not in trial
1406
assert trial["number"] == 0
1407
assert "params" not in trial
1410
@pytest.mark.skip_coverage
1411
def test_ask_sampler_kwargs_without_sampler() -> None:
1412
study_name = "test_study"
1414
'{"x": {"name": "FloatDistribution", "attributes": {"low": 0.0, "high": 1.0}}, '
1415
'"y": {"name": "CategoricalDistribution", "attributes": {"choices": ["foo"]}}}'
1418
with NamedTemporaryFilePool() as tf:
1419
db_url = "sqlite:///{}".format(tf.name)
1431
'{"multivariate": true}',
1434
result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1435
error_message = result.stderr.decode()
1436
assert "`--sampler_kwargs` is set without `--sampler`." in error_message
1439
@pytest.mark.skip_coverage
1440
@pytest.mark.parametrize(
1441
"direction,directions,sampler,sampler_kwargs",
1443
(None, None, None, None),
1444
("minimize", None, None, None),
1445
(None, "minimize maximize", None, None),
1446
(None, None, "RandomSampler", None),
1447
(None, None, "TPESampler", '{"multivariate": true}'),
1450
def test_create_study_and_ask(
1451
direction: Optional[str],
1452
directions: Optional[str],
1453
sampler: Optional[str],
1454
sampler_kwargs: Optional[str],
1456
study_name = "test_study"
1458
'{"x": {"name": "FloatDistribution", "attributes": {"low": 0.0, "high": 1.0}}, '
1459
'"y": {"name": "CategoricalDistribution", "attributes": {"choices": ["foo"]}}}'
1462
with NamedTemporaryFilePool() as tf:
1463
db_url = "sqlite:///{}".format(tf.name)
1465
create_study_args = [
1474
if direction is not None:
1475
create_study_args += ["--direction", direction]
1476
if directions is not None:
1477
create_study_args += ["--directions"] + directions.split()
1478
subprocess.check_call(create_study_args)
1491
if sampler is not None:
1492
args += ["--sampler", sampler]
1493
if sampler_kwargs is not None:
1494
args += ["--sampler-kwargs", sampler_kwargs]
1496
output = str(subprocess.check_output(args).decode().strip())
1497
trial = _parse_output(output, "json")
1499
assert trial["number"] == 0
1500
assert 0 <= trial["params"]["x"] <= 1
1501
assert trial["params"]["y"] == "foo"
1504
@pytest.mark.skip_coverage
1505
@pytest.mark.parametrize(
1506
"direction,directions,ask_direction,ask_directions",
1508
(None, None, "maximize", None),
1509
("minimize", None, "maximize", None),
1510
("minimize", None, None, "minimize minimize"),
1511
(None, "minimize maximize", None, "maximize minimize"),
1512
(None, "minimize maximize", "minimize", None),
1515
def test_create_study_and_ask_with_inconsistent_directions(
1516
direction: Optional[str],
1517
directions: Optional[str],
1518
ask_direction: Optional[str],
1519
ask_directions: Optional[str],
1521
study_name = "test_study"
1523
'{"x": {"name": "FloatDistribution", "attributes": {"low": 0.0, "high": 1.0}}, '
1524
'"y": {"name": "CategoricalDistribution", "attributes": {"choices": ["foo"]}}}'
1527
with NamedTemporaryFilePool() as tf:
1528
db_url = "sqlite:///{}".format(tf.name)
1530
create_study_args = [
1539
if direction is not None:
1540
create_study_args += ["--direction", direction]
1541
if directions is not None:
1542
create_study_args += ["--directions"] + directions.split()
1543
subprocess.check_call(create_study_args)
1555
if ask_direction is not None:
1556
args += ["--direction", ask_direction]
1557
if ask_directions is not None:
1558
args += ["--directions"] + ask_directions.split()
1560
result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1561
error_message = result.stderr.decode()
1562
assert "Cannot overwrite study direction" in error_message
1565
@pytest.mark.skip_coverage
1566
def test_ask_with_both_direction_and_directions() -> None:
1567
study_name = "test_study"
1569
'{"x": {"name": "FloatDistribution", "attributes": {"low": 0.0, "high": 1.0}}, '
1570
'"y": {"name": "CategoricalDistribution", "attributes": {"choices": ["foo"]}}}'
1573
with NamedTemporaryFilePool() as tf:
1574
db_url = "sqlite:///{}".format(tf.name)
1576
create_study_args = [
1584
subprocess.check_call(create_study_args)
1601
result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1602
error_message = result.stderr.decode()
1603
assert "Specify only one of `direction` and `directions`." in error_message
1606
@pytest.mark.skip_coverage
1607
def test_tell() -> None:
1608
study_name = "test_study"
1610
with NamedTemporaryFilePool() as tf:
1611
db_url = "sqlite:///{}".format(tf.name)
1613
output: Any = subprocess.check_output(
1625
output = output.decode("utf-8")
1626
output = json.loads(output)
1627
trial_number = output["number"]
1629
subprocess.check_output(
1642
study = optuna.load_study(storage=db_url, study_name=study_name)
1643
assert len(study.trials) == 1
1644
assert study.trials[0].state == TrialState.COMPLETE
1645
assert study.trials[0].values == [1.2]
1648
ret = subprocess.run(
1660
assert ret.returncode != 0
1663
subprocess.check_output(
1673
"--skip-if-finished",
1677
study = optuna.load_study(storage=db_url, study_name=study_name)
1678
assert len(study.trials) == 1
1679
assert study.trials[0].state == TrialState.COMPLETE
1680
assert study.trials[0].values == [1.2]
1683
@pytest.mark.skip_coverage
1684
def test_tell_with_nan() -> None:
1685
study_name = "test_study"
1687
with NamedTemporaryFilePool() as tf:
1688
db_url = "sqlite:///{}".format(tf.name)
1690
output: Any = subprocess.check_output(
1702
output = output.decode("utf-8")
1703
output = json.loads(output)
1704
trial_number = output["number"]
1706
subprocess.check_output(
1719
study = optuna.load_study(storage=db_url, study_name=study_name)
1720
assert len(study.trials) == 1
1721
assert study.trials[0].state == TrialState.FAIL
1722
assert study.trials[0].values is None
1725
@pytest.mark.skip_coverage
1726
@pytest.mark.parametrize(
1727
"verbosity, expected",
1729
("--verbose", True),
1733
def test_configure_logging_verbosity(verbosity: str, expected: bool) -> None:
1734
with StorageSupplier("sqlite") as storage:
1735
assert isinstance(storage, RDBStorage)
1736
storage_url = str(storage.engine.url)
1739
args = ["optuna", "create-study", "--storage", storage_url, verbosity]
1742
result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1743
error_message = result.stderr.decode()
1744
assert ("A new study created in RDB with name" in error_message) == expected