mirror of
https://github.com/wassname/ray.git
synced 2026-07-13 03:35:42 +08:00
Move more unit tests to bazel (#6250)
* move more unit tests to bazel * move to avoid conflict * fix lint * fix deps * seprate * fix failing tests * show tests * ignore mismatch * try combining bazel runs * build lint * remove tests from install * fix test utils * better config * split up * exclusive * fix verbosity * fix tests class * cleanup * remove flaky * fix metrics test * Update .travis.yml * no retry flaky * split up actor * split basic test * split up trial runner test * split stress * fix basic test * fix tests * switch to pytest runner for main * make microbench not fail * move load code to py3 * test is no longer package * bazel to end
This commit is contained in:
+38
-2
@@ -3,12 +3,14 @@ py_test(
|
||||
size = "medium",
|
||||
srcs = ["tests/test_actor_reuse.py"],
|
||||
tags = ["jenkins_only"],
|
||||
deps = [":tune_lib"],
|
||||
)
|
||||
|
||||
py_test(
|
||||
name = "test_automl_searcher",
|
||||
size = "small",
|
||||
srcs = ["tests/test_automl_searcher.py"],
|
||||
deps = [":tune_lib"],
|
||||
)
|
||||
|
||||
py_test(
|
||||
@@ -77,19 +79,52 @@ py_test(
|
||||
deps = [":tune_lib"],
|
||||
)
|
||||
|
||||
py_test(
|
||||
name = "test_run_experiment",
|
||||
size = "medium",
|
||||
srcs = ["tests/test_run_experiment.py"],
|
||||
deps = [":tune_lib"],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
|
||||
py_test(
|
||||
name = "test_trial_runner",
|
||||
size = "large",
|
||||
size = "medium",
|
||||
srcs = ["tests/test_trial_runner.py"],
|
||||
deps = [":tune_lib"],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
|
||||
py_test(
|
||||
name = "test_var",
|
||||
size = "medium",
|
||||
srcs = ["tests/test_var.py"],
|
||||
deps = [":tune_lib"],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
|
||||
py_test(
|
||||
name = "test_api",
|
||||
size = "medium",
|
||||
srcs = ["tests/test_api.py"],
|
||||
deps = [":tune_lib"],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
|
||||
py_test(
|
||||
name = "test_sync",
|
||||
size = "medium",
|
||||
srcs = ["tests/test_sync.py"],
|
||||
deps = [":tune_lib"],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
|
||||
py_test(
|
||||
name = "test_trial_scheduler",
|
||||
size = "medium",
|
||||
srcs = ["tests/test_trial_scheduler.py"],
|
||||
deps = [":tune_lib"],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
|
||||
py_test(
|
||||
@@ -113,11 +148,12 @@ py_test(
|
||||
size = "medium",
|
||||
srcs = ["tests/test_tune_server.py"],
|
||||
deps = [":tune_lib"],
|
||||
tags = ["exclusive"],
|
||||
)
|
||||
|
||||
# This is a dummy test dependency that causes the above tests to be
|
||||
# re-run if any of these files changes.
|
||||
py_library(
|
||||
name="tune_lib",
|
||||
name = "tune_lib",
|
||||
srcs = glob(["**/*.py"], exclude=["tests/*.py"]),
|
||||
)
|
||||
|
||||
@@ -96,4 +96,6 @@ class ActorReuseTest(unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
|
||||
@@ -0,0 +1,797 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import copy
|
||||
import os
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import ray
|
||||
from ray.rllib import _register_all
|
||||
|
||||
from ray import tune
|
||||
from ray.tune import Trainable, TuneError
|
||||
from ray.tune import register_env, register_trainable, run_experiments
|
||||
from ray.tune.schedulers import TrialScheduler, FIFOScheduler
|
||||
from ray.tune.trial import Trial
|
||||
from ray.tune.result import (TIMESTEPS_TOTAL, DONE, HOSTNAME, NODE_IP, PID,
|
||||
EPISODES_TOTAL, TRAINING_ITERATION,
|
||||
TIMESTEPS_THIS_ITER, TIME_THIS_ITER_S,
|
||||
TIME_TOTAL_S, TRIAL_ID, EXPERIMENT_TAG)
|
||||
from ray.tune.logger import Logger
|
||||
from ray.tune.util import pin_in_object_store, get_pinned_object, flatten_dict
|
||||
from ray.tune.experiment import Experiment
|
||||
from ray.tune.resources import Resources
|
||||
from ray.tune.suggest import grid_search
|
||||
from ray.tune.suggest.suggestion import _MockSuggestionAlgorithm
|
||||
|
||||
|
||||
class TrainableFunctionApiTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
ray.init(num_cpus=4, num_gpus=0, object_store_memory=150 * 1024 * 1024)
|
||||
|
||||
def tearDown(self):
|
||||
ray.shutdown()
|
||||
_register_all() # re-register the evicted objects
|
||||
|
||||
def checkAndReturnConsistentLogs(self, results, sleep_per_iter=None):
|
||||
"""Checks logging is the same between APIs.
|
||||
|
||||
Ignore "DONE" for logging but checks that the
|
||||
scheduler is notified properly with the last result.
|
||||
"""
|
||||
class_results = copy.deepcopy(results)
|
||||
function_results = copy.deepcopy(results)
|
||||
|
||||
class_output = []
|
||||
function_output = []
|
||||
scheduler_notif = []
|
||||
|
||||
class MockScheduler(FIFOScheduler):
|
||||
def on_trial_complete(self, runner, trial, result):
|
||||
scheduler_notif.append(result)
|
||||
|
||||
class ClassAPILogger(Logger):
|
||||
def on_result(self, result):
|
||||
class_output.append(result)
|
||||
|
||||
class FunctionAPILogger(Logger):
|
||||
def on_result(self, result):
|
||||
function_output.append(result)
|
||||
|
||||
class _WrappedTrainable(Trainable):
|
||||
def _setup(self, config):
|
||||
del config
|
||||
self._result_iter = copy.deepcopy(class_results)
|
||||
|
||||
def _train(self):
|
||||
if sleep_per_iter:
|
||||
time.sleep(sleep_per_iter)
|
||||
res = self._result_iter.pop(0) # This should not fail
|
||||
if not self._result_iter: # Mark "Done" for last result
|
||||
res[DONE] = True
|
||||
return res
|
||||
|
||||
def _function_trainable(config, reporter):
|
||||
for result in function_results:
|
||||
if sleep_per_iter:
|
||||
time.sleep(sleep_per_iter)
|
||||
reporter(**result)
|
||||
|
||||
class_trainable_name = "class_trainable"
|
||||
register_trainable(class_trainable_name, _WrappedTrainable)
|
||||
|
||||
trials = run_experiments(
|
||||
{
|
||||
"function_api": {
|
||||
"run": _function_trainable,
|
||||
"loggers": [FunctionAPILogger],
|
||||
},
|
||||
"class_api": {
|
||||
"run": class_trainable_name,
|
||||
"loggers": [ClassAPILogger],
|
||||
},
|
||||
},
|
||||
raise_on_failed_trial=False,
|
||||
scheduler=MockScheduler())
|
||||
|
||||
# Ignore these fields
|
||||
NO_COMPARE_FIELDS = {
|
||||
HOSTNAME,
|
||||
NODE_IP,
|
||||
TRIAL_ID,
|
||||
EXPERIMENT_TAG,
|
||||
PID,
|
||||
TIME_THIS_ITER_S,
|
||||
TIME_TOTAL_S,
|
||||
DONE, # This is ignored because FunctionAPI has different handling
|
||||
"timestamp",
|
||||
"time_since_restore",
|
||||
"experiment_id",
|
||||
"date",
|
||||
}
|
||||
|
||||
self.assertEqual(len(class_output), len(results))
|
||||
self.assertEqual(len(function_output), len(results))
|
||||
|
||||
def as_comparable_result(result):
|
||||
return {
|
||||
k: v
|
||||
for k, v in result.items() if k not in NO_COMPARE_FIELDS
|
||||
}
|
||||
|
||||
function_comparable = [
|
||||
as_comparable_result(result) for result in function_output
|
||||
]
|
||||
class_comparable = [
|
||||
as_comparable_result(result) for result in class_output
|
||||
]
|
||||
|
||||
self.assertEqual(function_comparable, class_comparable)
|
||||
|
||||
self.assertEqual(sum(t.get(DONE) for t in scheduler_notif), 2)
|
||||
self.assertEqual(
|
||||
as_comparable_result(scheduler_notif[0]),
|
||||
as_comparable_result(scheduler_notif[1]))
|
||||
|
||||
# Make sure the last result is the same.
|
||||
self.assertEqual(
|
||||
as_comparable_result(trials[0].last_result),
|
||||
as_comparable_result(trials[1].last_result))
|
||||
|
||||
return function_output, trials
|
||||
|
||||
def testPinObject(self):
|
||||
X = pin_in_object_store("hello")
|
||||
|
||||
@ray.remote
|
||||
def f():
|
||||
return get_pinned_object(X)
|
||||
|
||||
self.assertEqual(ray.get(f.remote()), "hello")
|
||||
|
||||
def testFetchPinned(self):
|
||||
X = pin_in_object_store("hello")
|
||||
|
||||
def train(config, reporter):
|
||||
get_pinned_object(X)
|
||||
reporter(timesteps_total=100, done=True)
|
||||
|
||||
register_trainable("f1", train)
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
}
|
||||
})
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result[TIMESTEPS_TOTAL], 100)
|
||||
|
||||
def testRegisterEnv(self):
|
||||
register_env("foo", lambda: None)
|
||||
self.assertRaises(TypeError, lambda: register_env("foo", 2))
|
||||
|
||||
def testRegisterEnvOverwrite(self):
|
||||
def train(config, reporter):
|
||||
reporter(timesteps_total=100, done=True)
|
||||
|
||||
def train2(config, reporter):
|
||||
reporter(timesteps_total=200, done=True)
|
||||
|
||||
register_trainable("f1", train)
|
||||
register_trainable("f1", train2)
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
}
|
||||
})
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result[TIMESTEPS_TOTAL], 200)
|
||||
|
||||
def testRegisterTrainable(self):
|
||||
def train(config, reporter):
|
||||
pass
|
||||
|
||||
class A(object):
|
||||
pass
|
||||
|
||||
class B(Trainable):
|
||||
pass
|
||||
|
||||
register_trainable("foo", train)
|
||||
Experiment("test", train)
|
||||
register_trainable("foo", B)
|
||||
Experiment("test", B)
|
||||
self.assertRaises(TypeError, lambda: register_trainable("foo", B()))
|
||||
self.assertRaises(TuneError, lambda: Experiment("foo", B()))
|
||||
self.assertRaises(TypeError, lambda: register_trainable("foo", A))
|
||||
self.assertRaises(TypeError, lambda: Experiment("foo", A))
|
||||
|
||||
def testTrainableCallable(self):
|
||||
def dummy_fn(config, reporter, steps):
|
||||
reporter(timesteps_total=steps, done=True)
|
||||
|
||||
from functools import partial
|
||||
steps = 500
|
||||
register_trainable("test", partial(dummy_fn, steps=steps))
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "test",
|
||||
}
|
||||
})
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result[TIMESTEPS_TOTAL], steps)
|
||||
[trial] = tune.run(partial(dummy_fn, steps=steps)).trials
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result[TIMESTEPS_TOTAL], steps)
|
||||
|
||||
def testBuiltInTrainableResources(self):
|
||||
class B(Trainable):
|
||||
@classmethod
|
||||
def default_resource_request(cls, config):
|
||||
return Resources(cpu=config["cpu"], gpu=config["gpu"])
|
||||
|
||||
def _train(self):
|
||||
return {"timesteps_this_iter": 1, "done": True}
|
||||
|
||||
register_trainable("B", B)
|
||||
|
||||
def f(cpus, gpus, queue_trials):
|
||||
return run_experiments(
|
||||
{
|
||||
"foo": {
|
||||
"run": "B",
|
||||
"config": {
|
||||
"cpu": cpus,
|
||||
"gpu": gpus,
|
||||
},
|
||||
}
|
||||
},
|
||||
queue_trials=queue_trials)[0]
|
||||
|
||||
# Should all succeed
|
||||
self.assertEqual(f(0, 0, False).status, Trial.TERMINATED)
|
||||
self.assertEqual(f(1, 0, True).status, Trial.TERMINATED)
|
||||
self.assertEqual(f(1, 0, True).status, Trial.TERMINATED)
|
||||
|
||||
# Too large resource request
|
||||
self.assertRaises(TuneError, lambda: f(100, 100, False))
|
||||
self.assertRaises(TuneError, lambda: f(0, 100, False))
|
||||
self.assertRaises(TuneError, lambda: f(100, 0, False))
|
||||
|
||||
# TODO(ekl) how can we test this is queued (hangs)?
|
||||
# f(100, 0, True)
|
||||
|
||||
def testRewriteEnv(self):
|
||||
def train(config, reporter):
|
||||
reporter(timesteps_total=1)
|
||||
|
||||
register_trainable("f1", train)
|
||||
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"env": "CartPole-v0",
|
||||
}
|
||||
})
|
||||
self.assertEqual(trial.config["env"], "CartPole-v0")
|
||||
|
||||
def testConfigPurity(self):
|
||||
def train(config, reporter):
|
||||
assert config == {"a": "b"}, config
|
||||
reporter(timesteps_total=1)
|
||||
|
||||
register_trainable("f1", train)
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"config": {
|
||||
"a": "b"
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
def testLogdir(self):
|
||||
def train(config, reporter):
|
||||
assert "/tmp/logdir/foo" in os.getcwd(), os.getcwd()
|
||||
reporter(timesteps_total=1)
|
||||
|
||||
register_trainable("f1", train)
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"local_dir": "/tmp/logdir",
|
||||
"config": {
|
||||
"a": "b"
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
def testLogdirStartingWithTilde(self):
|
||||
local_dir = "~/ray_results/local_dir"
|
||||
|
||||
def train(config, reporter):
|
||||
cwd = os.getcwd()
|
||||
assert cwd.startswith(os.path.expanduser(local_dir)), cwd
|
||||
assert not cwd.startswith("~"), cwd
|
||||
reporter(timesteps_total=1)
|
||||
|
||||
register_trainable("f1", train)
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"local_dir": local_dir,
|
||||
"config": {
|
||||
"a": "b"
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
def testLongFilename(self):
|
||||
def train(config, reporter):
|
||||
assert "/tmp/logdir/foo" in os.getcwd(), os.getcwd()
|
||||
reporter(timesteps_total=1)
|
||||
|
||||
register_trainable("f1", train)
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"local_dir": "/tmp/logdir",
|
||||
"config": {
|
||||
"a" * 50: tune.sample_from(lambda spec: 5.0 / 7),
|
||||
"b" * 50: tune.sample_from(lambda spec: "long" * 40),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
def testBadParams(self):
|
||||
def f():
|
||||
run_experiments({"foo": {}})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testBadParams2(self):
|
||||
def f():
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "asdf",
|
||||
"bah": "this param is not allowed",
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testBadParams3(self):
|
||||
def f():
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": grid_search("invalid grid search"),
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testBadParams4(self):
|
||||
def f():
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "asdf",
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testBadParams5(self):
|
||||
def f():
|
||||
run_experiments({"foo": {"run": "PPO", "stop": {"asdf": 1}}})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testBadParams6(self):
|
||||
def f():
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "PPO",
|
||||
"resources_per_trial": {
|
||||
"asdf": 1
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testBadStoppingReturn(self):
|
||||
def train(config, reporter):
|
||||
reporter()
|
||||
|
||||
register_trainable("f1", train)
|
||||
|
||||
def f():
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
"stop": {
|
||||
"time": 10
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testNestedStoppingReturn(self):
|
||||
def train(config, reporter):
|
||||
for i in range(10):
|
||||
reporter(test={"test1": {"test2": i}})
|
||||
|
||||
with self.assertRaises(TuneError):
|
||||
[trial] = tune.run(
|
||||
train, stop={
|
||||
"test": {
|
||||
"test1": {
|
||||
"test2": 6
|
||||
}
|
||||
}
|
||||
}).trials
|
||||
[trial] = tune.run(train, stop={"test/test1/test2": 6}).trials
|
||||
self.assertEqual(trial.last_result["training_iteration"], 7)
|
||||
|
||||
def testStoppingFunction(self):
|
||||
def train(config, reporter):
|
||||
for i in range(10):
|
||||
reporter(test=i)
|
||||
|
||||
def stop(trial_id, result):
|
||||
return result["test"] > 6
|
||||
|
||||
[trial] = tune.run(train, stop=stop).trials
|
||||
self.assertEqual(trial.last_result["training_iteration"], 8)
|
||||
|
||||
def testStoppingMemberFunction(self):
|
||||
def train(config, reporter):
|
||||
for i in range(10):
|
||||
reporter(test=i)
|
||||
|
||||
class Stopper:
|
||||
def stop(self, trial_id, result):
|
||||
return result["test"] > 6
|
||||
|
||||
[trial] = tune.run(train, stop=Stopper().stop).trials
|
||||
self.assertEqual(trial.last_result["training_iteration"], 8)
|
||||
|
||||
def testBadStoppingFunction(self):
|
||||
def train(config, reporter):
|
||||
for i in range(10):
|
||||
reporter(test=i)
|
||||
|
||||
class Stopper:
|
||||
def stop(self, result):
|
||||
return result["test"] > 6
|
||||
|
||||
def stop(result):
|
||||
return result["test"] > 6
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
tune.run(train, stop=Stopper().stop)
|
||||
with self.assertRaises(ValueError):
|
||||
tune.run(train, stop=stop)
|
||||
|
||||
def testEarlyReturn(self):
|
||||
def train(config, reporter):
|
||||
reporter(timesteps_total=100, done=True)
|
||||
time.sleep(99999)
|
||||
|
||||
register_trainable("f1", train)
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
}
|
||||
})
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result[TIMESTEPS_TOTAL], 100)
|
||||
|
||||
def testReporterNoUsage(self):
|
||||
def run_task(config, reporter):
|
||||
print("hello")
|
||||
|
||||
experiment = Experiment(run=run_task, name="ray_crash_repro")
|
||||
[trial] = ray.tune.run(experiment).trials
|
||||
print(trial.last_result)
|
||||
self.assertEqual(trial.last_result[DONE], True)
|
||||
|
||||
def testErrorReturn(self):
|
||||
def train(config, reporter):
|
||||
raise Exception("uh oh")
|
||||
|
||||
register_trainable("f1", train)
|
||||
|
||||
def f():
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaises(TuneError, f)
|
||||
|
||||
def testSuccess(self):
|
||||
def train(config, reporter):
|
||||
for i in range(100):
|
||||
reporter(timesteps_total=i)
|
||||
|
||||
register_trainable("f1", train)
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
}
|
||||
})
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result[TIMESTEPS_TOTAL], 99)
|
||||
|
||||
def testNoRaiseFlag(self):
|
||||
def train(config, reporter):
|
||||
raise Exception()
|
||||
|
||||
register_trainable("f1", train)
|
||||
|
||||
[trial] = run_experiments(
|
||||
{
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
}
|
||||
}, raise_on_failed_trial=False)
|
||||
self.assertEqual(trial.status, Trial.ERROR)
|
||||
|
||||
def testReportInfinity(self):
|
||||
def train(config, reporter):
|
||||
for i in range(100):
|
||||
reporter(mean_accuracy=float("inf"))
|
||||
|
||||
register_trainable("f1", train)
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
}
|
||||
})
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result["mean_accuracy"], float("inf"))
|
||||
|
||||
def testNestedResults(self):
|
||||
def create_result(i):
|
||||
return {"test": {"1": {"2": {"3": i, "4": False}}}}
|
||||
|
||||
flattened_keys = list(flatten_dict(create_result(0)))
|
||||
|
||||
class _MockScheduler(FIFOScheduler):
|
||||
results = []
|
||||
|
||||
def on_trial_result(self, trial_runner, trial, result):
|
||||
self.results += [result]
|
||||
return TrialScheduler.CONTINUE
|
||||
|
||||
def on_trial_complete(self, trial_runner, trial, result):
|
||||
self.complete_result = result
|
||||
|
||||
def train(config, reporter):
|
||||
for i in range(100):
|
||||
reporter(**create_result(i))
|
||||
|
||||
algo = _MockSuggestionAlgorithm()
|
||||
scheduler = _MockScheduler()
|
||||
[trial] = tune.run(
|
||||
train,
|
||||
scheduler=scheduler,
|
||||
search_alg=algo,
|
||||
stop={
|
||||
"test/1/2/3": 20
|
||||
}).trials
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result["test"]["1"]["2"]["3"], 20)
|
||||
self.assertEqual(trial.last_result["test"]["1"]["2"]["4"], False)
|
||||
self.assertEqual(trial.last_result[TRAINING_ITERATION], 21)
|
||||
self.assertEqual(len(scheduler.results), 20)
|
||||
self.assertTrue(
|
||||
all(
|
||||
set(result) >= set(flattened_keys)
|
||||
for result in scheduler.results))
|
||||
self.assertTrue(set(scheduler.complete_result) >= set(flattened_keys))
|
||||
self.assertEqual(len(algo.results), 20)
|
||||
self.assertTrue(
|
||||
all(set(result) >= set(flattened_keys) for result in algo.results))
|
||||
with self.assertRaises(TuneError):
|
||||
[trial] = tune.run(train, stop={"1/2/3": 20})
|
||||
with self.assertRaises(TuneError):
|
||||
[trial] = tune.run(train, stop={"test": 1}).trials
|
||||
|
||||
def testReportTimeStep(self):
|
||||
# Test that no timestep count are logged if never the Trainable never
|
||||
# returns any.
|
||||
results1 = [dict(mean_accuracy=5, done=i == 99) for i in range(100)]
|
||||
logs1, _ = self.checkAndReturnConsistentLogs(results1)
|
||||
|
||||
self.assertTrue(all(log[TIMESTEPS_TOTAL] is None for log in logs1))
|
||||
|
||||
# Test that no timesteps_this_iter are logged if only timesteps_total
|
||||
# are returned.
|
||||
results2 = [dict(timesteps_total=5, done=i == 9) for i in range(10)]
|
||||
logs2, _ = self.checkAndReturnConsistentLogs(results2)
|
||||
|
||||
# Re-run the same trials but with added delay. This is to catch some
|
||||
# inconsistent timestep counting that was present in the multi-threaded
|
||||
# FunctionRunner. This part of the test can be removed once the
|
||||
# multi-threaded FunctionRunner is removed from ray/tune.
|
||||
# TODO: remove once the multi-threaded function runner is gone.
|
||||
logs2, _ = self.checkAndReturnConsistentLogs(results2, 0.5)
|
||||
|
||||
# check all timesteps_total report the same value
|
||||
self.assertTrue(all(log[TIMESTEPS_TOTAL] == 5 for log in logs2))
|
||||
# check that none of the logs report timesteps_this_iter
|
||||
self.assertFalse(
|
||||
any(hasattr(log, TIMESTEPS_THIS_ITER) for log in logs2))
|
||||
|
||||
# Test that timesteps_total and episodes_total are reported when
|
||||
# timesteps_this_iter and episodes_this_iter despite only return zeros.
|
||||
results3 = [
|
||||
dict(timesteps_this_iter=0, episodes_this_iter=0)
|
||||
for i in range(10)
|
||||
]
|
||||
logs3, _ = self.checkAndReturnConsistentLogs(results3)
|
||||
|
||||
self.assertTrue(all(log[TIMESTEPS_TOTAL] == 0 for log in logs3))
|
||||
self.assertTrue(all(log[EPISODES_TOTAL] == 0 for log in logs3))
|
||||
|
||||
# Test that timesteps_total and episodes_total are properly counted
|
||||
# when timesteps_this_iter and episodes_this_iter report non-zero
|
||||
# values.
|
||||
results4 = [
|
||||
dict(timesteps_this_iter=3, episodes_this_iter=i)
|
||||
for i in range(10)
|
||||
]
|
||||
logs4, _ = self.checkAndReturnConsistentLogs(results4)
|
||||
|
||||
# The last reported result should not be double-logged.
|
||||
self.assertEqual(logs4[-1][TIMESTEPS_TOTAL], 30)
|
||||
self.assertNotEqual(logs4[-2][TIMESTEPS_TOTAL],
|
||||
logs4[-1][TIMESTEPS_TOTAL])
|
||||
self.assertEqual(logs4[-1][EPISODES_TOTAL], 45)
|
||||
self.assertNotEqual(logs4[-2][EPISODES_TOTAL],
|
||||
logs4[-1][EPISODES_TOTAL])
|
||||
|
||||
def testAllValuesReceived(self):
|
||||
results1 = [
|
||||
dict(timesteps_total=(i + 1), my_score=i**2, done=i == 4)
|
||||
for i in range(5)
|
||||
]
|
||||
|
||||
logs1, _ = self.checkAndReturnConsistentLogs(results1)
|
||||
|
||||
# check if the correct number of results were reported
|
||||
self.assertEqual(len(logs1), len(results1))
|
||||
|
||||
def check_no_missing(reported_result, result):
|
||||
common_results = [reported_result[k] == result[k] for k in result]
|
||||
return all(common_results)
|
||||
|
||||
# check that no result was dropped or modified
|
||||
complete_results = [
|
||||
check_no_missing(log, result)
|
||||
for log, result in zip(logs1, results1)
|
||||
]
|
||||
self.assertTrue(all(complete_results))
|
||||
|
||||
# check if done was logged exactly once
|
||||
self.assertEqual(len([r for r in logs1 if r.get("done")]), 1)
|
||||
|
||||
def testNoDoneReceived(self):
|
||||
# repeat same test but without explicitly reporting done=True
|
||||
results1 = [
|
||||
dict(timesteps_total=(i + 1), my_score=i**2) for i in range(5)
|
||||
]
|
||||
|
||||
logs1, trials = self.checkAndReturnConsistentLogs(results1)
|
||||
|
||||
# check if the correct number of results were reported.
|
||||
self.assertEqual(len(logs1), len(results1))
|
||||
|
||||
def check_no_missing(reported_result, result):
|
||||
common_results = [reported_result[k] == result[k] for k in result]
|
||||
return all(common_results)
|
||||
|
||||
# check that no result was dropped or modified
|
||||
complete_results1 = [
|
||||
check_no_missing(log, result)
|
||||
for log, result in zip(logs1, results1)
|
||||
]
|
||||
self.assertTrue(all(complete_results1))
|
||||
|
||||
def testCheckpointDict(self):
|
||||
class TestTrain(Trainable):
|
||||
def _setup(self, config):
|
||||
self.state = {"hi": 1}
|
||||
|
||||
def _train(self):
|
||||
return {"timesteps_this_iter": 1, "done": True}
|
||||
|
||||
def _save(self, path):
|
||||
return self.state
|
||||
|
||||
def _restore(self, state):
|
||||
self.state = state
|
||||
|
||||
test_trainable = TestTrain()
|
||||
result = test_trainable.save()
|
||||
test_trainable.state["hi"] = 2
|
||||
test_trainable.restore(result)
|
||||
self.assertEqual(test_trainable.state["hi"], 1)
|
||||
|
||||
trials = run_experiments({
|
||||
"foo": {
|
||||
"run": TestTrain,
|
||||
"checkpoint_at_end": True
|
||||
}
|
||||
})
|
||||
for trial in trials:
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertTrue(trial.has_checkpoint())
|
||||
|
||||
def testMultipleCheckpoints(self):
|
||||
class TestTrain(Trainable):
|
||||
def _setup(self, config):
|
||||
self.state = {"hi": 1, "iter": 0}
|
||||
|
||||
def _train(self):
|
||||
self.state["iter"] += 1
|
||||
return {"timesteps_this_iter": 1, "done": True}
|
||||
|
||||
def _save(self, path):
|
||||
return self.state
|
||||
|
||||
def _restore(self, state):
|
||||
self.state = state
|
||||
|
||||
test_trainable = TestTrain()
|
||||
checkpoint_1 = test_trainable.save()
|
||||
test_trainable.train()
|
||||
checkpoint_2 = test_trainable.save()
|
||||
self.assertNotEqual(checkpoint_1, checkpoint_2)
|
||||
test_trainable.restore(checkpoint_2)
|
||||
self.assertEqual(test_trainable.state["iter"], 1)
|
||||
test_trainable.restore(checkpoint_1)
|
||||
self.assertEqual(test_trainable.state["iter"], 0)
|
||||
|
||||
trials = run_experiments({
|
||||
"foo": {
|
||||
"run": TestTrain,
|
||||
"checkpoint_at_end": True
|
||||
}
|
||||
})
|
||||
for trial in trials:
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertTrue(trial.has_checkpoint())
|
||||
|
||||
def testIterationCounter(self):
|
||||
def train(config, reporter):
|
||||
for i in range(100):
|
||||
reporter(itr=i, timesteps_this_iter=1)
|
||||
|
||||
register_trainable("exp", train)
|
||||
config = {
|
||||
"my_exp": {
|
||||
"run": "exp",
|
||||
"config": {
|
||||
"iterations": 100,
|
||||
},
|
||||
"stop": {
|
||||
"timesteps_total": 100
|
||||
},
|
||||
}
|
||||
}
|
||||
[trial] = run_experiments(config)
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result[TRAINING_ITERATION], 100)
|
||||
self.assertEqual(trial.last_result["itr"], 99)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
@@ -70,4 +70,6 @@ class AutoMLSearcherTest(unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
|
||||
@@ -109,4 +109,4 @@ class CheckpointManagerTest(unittest.TestCase):
|
||||
if __name__ == "__main__":
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", "-s", __file__]))
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
|
||||
@@ -13,8 +13,8 @@ import sys
|
||||
import ray
|
||||
from ray import tune
|
||||
from ray.rllib import _register_all
|
||||
from ray.tests.cluster_utils import Cluster
|
||||
from ray.tests.utils import run_string_as_driver_nonblocking
|
||||
from ray.cluster_utils import Cluster
|
||||
from ray.test_utils import run_string_as_driver_nonblocking
|
||||
from ray.tune.error import TuneError
|
||||
from ray.tune.ray_trial_executor import RayTrialExecutor
|
||||
from ray.tune.experiment import Experiment
|
||||
@@ -598,4 +598,4 @@ tune.run(
|
||||
if __name__ == "__main__":
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", "-s", __file__]))
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
|
||||
@@ -62,4 +62,6 @@ class ExperimentTest(unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
|
||||
@@ -204,4 +204,6 @@ class AnalysisSuite(unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
|
||||
@@ -64,4 +64,6 @@ class LoggerSuite(unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
|
||||
@@ -14,7 +14,7 @@ from ray.tune.registry import _global_registry, TRAINABLE_CLASS
|
||||
from ray.tune.suggest import BasicVariantGenerator
|
||||
from ray.tune.trial import Trial, Checkpoint
|
||||
from ray.tune.resources import Resources
|
||||
from ray.tests.cluster_utils import Cluster
|
||||
from ray.cluster_utils import Cluster
|
||||
|
||||
|
||||
class RayTrialExecutorTest(unittest.TestCase):
|
||||
@@ -190,4 +190,6 @@ class LocalModeExecutorTest(RayTrialExecutorTest):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import ray
|
||||
from ray.rllib import _register_all
|
||||
|
||||
from ray.tune.result import TIMESTEPS_TOTAL
|
||||
from ray.tune import Trainable, TuneError
|
||||
from ray.tune import register_trainable, run_experiments
|
||||
from ray.tune.logger import Logger
|
||||
from ray.tune.experiment import Experiment
|
||||
from ray.tune.trial import Trial, ExportFormat
|
||||
|
||||
|
||||
class RunExperimentTest(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
ray.shutdown()
|
||||
_register_all() # re-register the evicted objects
|
||||
|
||||
def testDict(self):
|
||||
def train(config, reporter):
|
||||
for i in range(100):
|
||||
reporter(timesteps_total=i)
|
||||
|
||||
register_trainable("f1", train)
|
||||
trials = run_experiments({
|
||||
"foo": {
|
||||
"run": "f1",
|
||||
},
|
||||
"bar": {
|
||||
"run": "f1",
|
||||
}
|
||||
})
|
||||
for trial in trials:
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result[TIMESTEPS_TOTAL], 99)
|
||||
|
||||
def testExperiment(self):
|
||||
def train(config, reporter):
|
||||
for i in range(100):
|
||||
reporter(timesteps_total=i)
|
||||
|
||||
register_trainable("f1", train)
|
||||
exp1 = Experiment(**{
|
||||
"name": "foo",
|
||||
"run": "f1",
|
||||
})
|
||||
[trial] = run_experiments(exp1)
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result[TIMESTEPS_TOTAL], 99)
|
||||
|
||||
def testExperimentList(self):
|
||||
def train(config, reporter):
|
||||
for i in range(100):
|
||||
reporter(timesteps_total=i)
|
||||
|
||||
register_trainable("f1", train)
|
||||
exp1 = Experiment(**{
|
||||
"name": "foo",
|
||||
"run": "f1",
|
||||
})
|
||||
exp2 = Experiment(**{
|
||||
"name": "bar",
|
||||
"run": "f1",
|
||||
})
|
||||
trials = run_experiments([exp1, exp2])
|
||||
for trial in trials:
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertEqual(trial.last_result[TIMESTEPS_TOTAL], 99)
|
||||
|
||||
def testAutoregisterTrainable(self):
|
||||
def train(config, reporter):
|
||||
for i in range(100):
|
||||
reporter(timesteps_total=i)
|
||||
|
||||
class B(Trainable):
|
||||
def _train(self):
|
||||
return {"timesteps_this_iter": 1, "done": True}
|
||||
|
||||
register_trainable("f1", train)
|
||||
trials = run_experiments({
|
||||
"foo": {
|
||||
"run": train,
|
||||
},
|
||||
"bar": {
|
||||
"run": B
|
||||
}
|
||||
})
|
||||
for trial in trials:
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
|
||||
def testCheckpointAtEnd(self):
|
||||
class train(Trainable):
|
||||
def _train(self):
|
||||
return {"timesteps_this_iter": 1, "done": True}
|
||||
|
||||
def _save(self, path):
|
||||
checkpoint = path + "/checkpoint"
|
||||
with open(checkpoint, "w") as f:
|
||||
f.write("OK")
|
||||
return checkpoint
|
||||
|
||||
trials = run_experiments({
|
||||
"foo": {
|
||||
"run": train,
|
||||
"checkpoint_at_end": True
|
||||
}
|
||||
})
|
||||
for trial in trials:
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertTrue(trial.has_checkpoint())
|
||||
|
||||
def testExportFormats(self):
|
||||
class train(Trainable):
|
||||
def _train(self):
|
||||
return {"timesteps_this_iter": 1, "done": True}
|
||||
|
||||
def _export_model(self, export_formats, export_dir):
|
||||
path = export_dir + "/exported"
|
||||
with open(path, "w") as f:
|
||||
f.write("OK")
|
||||
return {export_formats[0]: path}
|
||||
|
||||
trials = run_experiments({
|
||||
"foo": {
|
||||
"run": train,
|
||||
"export_formats": ["format"]
|
||||
}
|
||||
})
|
||||
for trial in trials:
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(trial.logdir, "exported")))
|
||||
|
||||
def testInvalidExportFormats(self):
|
||||
class train(Trainable):
|
||||
def _train(self):
|
||||
return {"timesteps_this_iter": 1, "done": True}
|
||||
|
||||
def _export_model(self, export_formats, export_dir):
|
||||
ExportFormat.validate(export_formats)
|
||||
return {}
|
||||
|
||||
def fail_trial():
|
||||
run_experiments({
|
||||
"foo": {
|
||||
"run": train,
|
||||
"export_formats": ["format"]
|
||||
}
|
||||
})
|
||||
|
||||
self.assertRaises(TuneError, fail_trial)
|
||||
|
||||
def testCustomResources(self):
|
||||
ray.shutdown()
|
||||
ray.init(resources={"hi": 3})
|
||||
|
||||
class train(Trainable):
|
||||
def _train(self):
|
||||
return {"timesteps_this_iter": 1, "done": True}
|
||||
|
||||
trials = run_experiments({
|
||||
"foo": {
|
||||
"run": train,
|
||||
"resources_per_trial": {
|
||||
"cpu": 1,
|
||||
"custom_resources": {
|
||||
"hi": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
for trial in trials:
|
||||
self.assertEqual(trial.status, Trial.TERMINATED)
|
||||
|
||||
def testCustomLogger(self):
|
||||
class CustomLogger(Logger):
|
||||
def on_result(self, result):
|
||||
with open(os.path.join(self.logdir, "test.log"), "w") as f:
|
||||
f.write("hi")
|
||||
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "__fake",
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"loggers": [CustomLogger]
|
||||
}
|
||||
})
|
||||
self.assertTrue(os.path.exists(os.path.join(trial.logdir, "test.log")))
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(trial.logdir, "params.json")))
|
||||
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "__fake",
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
}
|
||||
}
|
||||
})
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(trial.logdir, "params.json")))
|
||||
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "__fake",
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"loggers": []
|
||||
}
|
||||
})
|
||||
self.assertFalse(
|
||||
os.path.exists(os.path.join(trial.logdir, "params.json")))
|
||||
|
||||
def testCustomTrialString(self):
|
||||
[trial] = run_experiments({
|
||||
"foo": {
|
||||
"run": "__fake",
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"trial_name_creator":
|
||||
lambda t: "{}_{}_321".format(t.trainable_name, t.trial_id)
|
||||
}
|
||||
})
|
||||
self.assertEquals(
|
||||
str(trial), "{}_{}_321".format(trial.trainable_name,
|
||||
trial.trial_id))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
@@ -0,0 +1,218 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import glob
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import ray
|
||||
from ray.rllib import _register_all
|
||||
|
||||
from ray import tune
|
||||
from ray.tune import TuneError
|
||||
from ray.tune.syncer import CommandBasedClient
|
||||
|
||||
if sys.version_info >= (3, 3):
|
||||
from unittest.mock import patch
|
||||
else:
|
||||
from mock import patch
|
||||
|
||||
|
||||
class TestSyncFunctionality(unittest.TestCase):
|
||||
def setUp(self):
|
||||
ray.init()
|
||||
|
||||
def tearDown(self):
|
||||
ray.shutdown()
|
||||
_register_all() # re-register the evicted objects
|
||||
|
||||
@patch("ray.tune.syncer.S3_PREFIX", "test")
|
||||
def testNoUploadDir(self):
|
||||
"""No Upload Dir is given."""
|
||||
with self.assertRaises(AssertionError):
|
||||
[trial] = tune.run(
|
||||
"__fake",
|
||||
name="foo",
|
||||
max_failures=0,
|
||||
**{
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"sync_to_cloud": "echo {source} {target}"
|
||||
}).trials
|
||||
|
||||
@patch("ray.tune.syncer.S3_PREFIX", "test")
|
||||
def testCloudProperString(self):
|
||||
with self.assertRaises(ValueError):
|
||||
[trial] = tune.run(
|
||||
"__fake",
|
||||
name="foo",
|
||||
max_failures=0,
|
||||
**{
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"upload_dir": "test",
|
||||
"sync_to_cloud": "ls {target}"
|
||||
}).trials
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
[trial] = tune.run(
|
||||
"__fake",
|
||||
name="foo",
|
||||
max_failures=0,
|
||||
**{
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"upload_dir": "test",
|
||||
"sync_to_cloud": "ls {source}"
|
||||
}).trials
|
||||
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
logfile = os.path.join(tmpdir, "test.log")
|
||||
|
||||
[trial] = tune.run(
|
||||
"__fake",
|
||||
name="foo",
|
||||
max_failures=0,
|
||||
**{
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"upload_dir": "test",
|
||||
"sync_to_cloud": "echo {source} {target} > " + logfile
|
||||
}).trials
|
||||
with open(logfile) as f:
|
||||
lines = f.read()
|
||||
self.assertTrue("test" in lines)
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
def testClusterProperString(self):
|
||||
"""Tests that invalid commands throw.."""
|
||||
with self.assertRaises(TuneError):
|
||||
# This raises TuneError because logger is init in safe zone.
|
||||
[trial] = tune.run(
|
||||
"__fake",
|
||||
name="foo",
|
||||
max_failures=0,
|
||||
**{
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"sync_to_driver": "ls {target}"
|
||||
}).trials
|
||||
|
||||
with self.assertRaises(TuneError):
|
||||
# This raises TuneError because logger is init in safe zone.
|
||||
[trial] = tune.run(
|
||||
"__fake",
|
||||
name="foo",
|
||||
max_failures=0,
|
||||
**{
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"sync_to_driver": "ls {source}"
|
||||
}).trials
|
||||
|
||||
with patch.object(CommandBasedClient, "execute") as mock_fn:
|
||||
with patch("ray.services.get_node_ip_address") as mock_sync:
|
||||
mock_sync.return_value = "0.0.0.0"
|
||||
[trial] = tune.run(
|
||||
"__fake",
|
||||
name="foo",
|
||||
max_failures=0,
|
||||
**{
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"sync_to_driver": "echo {source} {target}"
|
||||
}).trials
|
||||
self.assertGreater(mock_fn.call_count, 0)
|
||||
|
||||
def testCloudFunctions(self):
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
tmpdir2 = tempfile.mkdtemp()
|
||||
os.mkdir(os.path.join(tmpdir2, "foo"))
|
||||
|
||||
def sync_func(local, remote):
|
||||
for filename in glob.glob(os.path.join(local, "*.json")):
|
||||
shutil.copy(filename, remote)
|
||||
|
||||
[trial] = tune.run(
|
||||
"__fake",
|
||||
name="foo",
|
||||
max_failures=0,
|
||||
local_dir=tmpdir,
|
||||
stop={
|
||||
"training_iteration": 1
|
||||
},
|
||||
upload_dir=tmpdir2,
|
||||
sync_to_cloud=sync_func).trials
|
||||
test_file_path = glob.glob(os.path.join(tmpdir2, "foo", "*.json"))
|
||||
self.assertTrue(test_file_path)
|
||||
shutil.rmtree(tmpdir)
|
||||
shutil.rmtree(tmpdir2)
|
||||
|
||||
def testClusterSyncFunction(self):
|
||||
def sync_func_driver(source, target):
|
||||
assert ":" in source, "Source {} not a remote path.".format(source)
|
||||
assert ":" not in target, "Target is supposed to be local."
|
||||
with open(os.path.join(target, "test.log2"), "w") as f:
|
||||
print("writing to", f.name)
|
||||
f.write(source)
|
||||
|
||||
[trial] = tune.run(
|
||||
"__fake",
|
||||
name="foo",
|
||||
max_failures=0,
|
||||
stop={
|
||||
"training_iteration": 1
|
||||
},
|
||||
sync_to_driver=sync_func_driver).trials
|
||||
test_file_path = os.path.join(trial.logdir, "test.log2")
|
||||
self.assertFalse(os.path.exists(test_file_path))
|
||||
|
||||
with patch("ray.services.get_node_ip_address") as mock_sync:
|
||||
mock_sync.return_value = "0.0.0.0"
|
||||
[trial] = tune.run(
|
||||
"__fake",
|
||||
name="foo",
|
||||
max_failures=0,
|
||||
stop={
|
||||
"training_iteration": 1
|
||||
},
|
||||
sync_to_driver=sync_func_driver).trials
|
||||
test_file_path = os.path.join(trial.logdir, "test.log2")
|
||||
self.assertTrue(os.path.exists(test_file_path))
|
||||
os.remove(test_file_path)
|
||||
|
||||
def testNoSync(self):
|
||||
"""Sync should not run on a single node."""
|
||||
|
||||
def sync_func(source, target):
|
||||
pass
|
||||
|
||||
with patch.object(CommandBasedClient, "execute") as mock_sync:
|
||||
[trial] = tune.run(
|
||||
"__fake",
|
||||
name="foo",
|
||||
max_failures=0,
|
||||
**{
|
||||
"stop": {
|
||||
"training_iteration": 1
|
||||
},
|
||||
"sync_to_driver": sync_func
|
||||
}).trials
|
||||
self.assertEqual(mock_sync.call_count, 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
@@ -85,4 +85,6 @@ class TrackApiTest(unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1194,4 +1194,6 @@ class AsyncHyperBandSuite(unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
|
||||
@@ -13,7 +13,7 @@ import numpy as np
|
||||
|
||||
import ray
|
||||
from ray import tune
|
||||
from ray.tests.utils import recursive_fnmatch
|
||||
from ray.test_utils import recursive_fnmatch
|
||||
from ray.tune.util import validate_save_restore
|
||||
from ray.rllib import _register_all
|
||||
from ray.tune.suggest.hyperopt import HyperOptSearch
|
||||
@@ -277,4 +277,6 @@ class SigOptWarmStartTest(AbstractWarmStartTest, unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
|
||||
@@ -151,4 +151,6 @@ class SerialTuneRelativeLocalDirTest(unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
|
||||
@@ -144,4 +144,6 @@ class TuneServerSuite(unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
|
||||
@@ -0,0 +1,319 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import numpy as np
|
||||
import unittest
|
||||
|
||||
import ray
|
||||
from ray.rllib import _register_all
|
||||
|
||||
from ray import tune
|
||||
from ray.tune.result import DEFAULT_RESULTS_DIR
|
||||
from ray.tune.experiment import Experiment
|
||||
from ray.tune.suggest import grid_search, BasicVariantGenerator
|
||||
from ray.tune.suggest.suggestion import _MockSuggestionAlgorithm
|
||||
from ray.tune.suggest.variant_generator import (RecursiveDependencyError,
|
||||
resolve_nested_dict)
|
||||
|
||||
|
||||
class VariantGeneratorTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
ray.init()
|
||||
|
||||
def tearDown(self):
|
||||
ray.shutdown()
|
||||
_register_all() # re-register the evicted objects
|
||||
|
||||
def generate_trials(self, spec, name):
|
||||
suggester = BasicVariantGenerator()
|
||||
suggester.add_configurations({name: spec})
|
||||
return suggester.next_trials()
|
||||
|
||||
def testParseToTrials(self):
|
||||
trials = self.generate_trials({
|
||||
"run": "PPO",
|
||||
"num_samples": 2,
|
||||
"max_failures": 5,
|
||||
"config": {
|
||||
"env": "Pong-v0",
|
||||
"foo": "bar"
|
||||
},
|
||||
}, "tune-pong")
|
||||
trials = list(trials)
|
||||
self.assertEqual(len(trials), 2)
|
||||
self.assertTrue("PPO_Pong-v0" in str(trials[0]))
|
||||
self.assertEqual(trials[0].config, {"foo": "bar", "env": "Pong-v0"})
|
||||
self.assertEqual(trials[0].trainable_name, "PPO")
|
||||
self.assertEqual(trials[0].experiment_tag, "0")
|
||||
self.assertEqual(trials[0].max_failures, 5)
|
||||
self.assertEqual(trials[0].evaluated_params, {})
|
||||
self.assertEqual(trials[0].local_dir,
|
||||
os.path.join(DEFAULT_RESULTS_DIR, "tune-pong"))
|
||||
self.assertEqual(trials[1].experiment_tag, "1")
|
||||
|
||||
def testEval(self):
|
||||
trials = self.generate_trials({
|
||||
"run": "PPO",
|
||||
"config": {
|
||||
"foo": {
|
||||
"eval": "2 + 2"
|
||||
},
|
||||
},
|
||||
}, "eval")
|
||||
trials = list(trials)
|
||||
self.assertEqual(len(trials), 1)
|
||||
self.assertEqual(trials[0].config, {"foo": 4})
|
||||
self.assertEqual(trials[0].evaluated_params, {"foo": 4})
|
||||
self.assertEqual(trials[0].experiment_tag, "0_foo=4")
|
||||
|
||||
def testGridSearch(self):
|
||||
trials = self.generate_trials({
|
||||
"run": "PPO",
|
||||
"config": {
|
||||
"bar": {
|
||||
"grid_search": [True, False]
|
||||
},
|
||||
"foo": {
|
||||
"grid_search": [1, 2, 3]
|
||||
},
|
||||
"baz": "asd",
|
||||
},
|
||||
}, "grid_search")
|
||||
trials = list(trials)
|
||||
self.assertEqual(len(trials), 6)
|
||||
self.assertEqual(trials[0].config, {
|
||||
"bar": True,
|
||||
"foo": 1,
|
||||
"baz": "asd",
|
||||
})
|
||||
self.assertEqual(trials[0].evaluated_params, {
|
||||
"bar": True,
|
||||
"foo": 1,
|
||||
})
|
||||
self.assertEqual(trials[0].experiment_tag, "0_bar=True,foo=1")
|
||||
|
||||
self.assertEqual(trials[1].config, {
|
||||
"bar": False,
|
||||
"foo": 1,
|
||||
"baz": "asd",
|
||||
})
|
||||
self.assertEqual(trials[1].evaluated_params, {
|
||||
"bar": False,
|
||||
"foo": 1,
|
||||
})
|
||||
self.assertEqual(trials[1].experiment_tag, "1_bar=False,foo=1")
|
||||
|
||||
self.assertEqual(trials[2].config, {
|
||||
"bar": True,
|
||||
"foo": 2,
|
||||
"baz": "asd",
|
||||
})
|
||||
self.assertEqual(trials[2].evaluated_params, {
|
||||
"bar": True,
|
||||
"foo": 2,
|
||||
})
|
||||
|
||||
self.assertEqual(trials[3].config, {
|
||||
"bar": False,
|
||||
"foo": 2,
|
||||
"baz": "asd",
|
||||
})
|
||||
self.assertEqual(trials[3].evaluated_params, {
|
||||
"bar": False,
|
||||
"foo": 2,
|
||||
})
|
||||
|
||||
self.assertEqual(trials[4].config, {
|
||||
"bar": True,
|
||||
"foo": 3,
|
||||
"baz": "asd",
|
||||
})
|
||||
self.assertEqual(trials[4].evaluated_params, {
|
||||
"bar": True,
|
||||
"foo": 3,
|
||||
})
|
||||
|
||||
self.assertEqual(trials[5].config, {
|
||||
"bar": False,
|
||||
"foo": 3,
|
||||
"baz": "asd",
|
||||
})
|
||||
self.assertEqual(trials[5].evaluated_params, {
|
||||
"bar": False,
|
||||
"foo": 3,
|
||||
})
|
||||
|
||||
def testGridSearchAndEval(self):
|
||||
trials = self.generate_trials({
|
||||
"run": "PPO",
|
||||
"config": {
|
||||
"qux": tune.sample_from(lambda spec: 2 + 2),
|
||||
"bar": grid_search([True, False]),
|
||||
"foo": grid_search([1, 2, 3]),
|
||||
"baz": "asd",
|
||||
},
|
||||
}, "grid_eval")
|
||||
trials = list(trials)
|
||||
self.assertEqual(len(trials), 6)
|
||||
self.assertEqual(trials[0].config, {
|
||||
"bar": True,
|
||||
"foo": 1,
|
||||
"qux": 4,
|
||||
"baz": "asd",
|
||||
})
|
||||
self.assertEqual(trials[0].evaluated_params, {
|
||||
"bar": True,
|
||||
"foo": 1,
|
||||
"qux": 4,
|
||||
})
|
||||
self.assertEqual(trials[0].experiment_tag, "0_bar=True,foo=1,qux=4")
|
||||
|
||||
def testConditionResolution(self):
|
||||
trials = self.generate_trials({
|
||||
"run": "PPO",
|
||||
"config": {
|
||||
"x": 1,
|
||||
"y": tune.sample_from(lambda spec: spec.config.x + 1),
|
||||
"z": tune.sample_from(lambda spec: spec.config.y + 1),
|
||||
},
|
||||
}, "condition_resolution")
|
||||
trials = list(trials)
|
||||
self.assertEqual(len(trials), 1)
|
||||
self.assertEqual(trials[0].config, {"x": 1, "y": 2, "z": 3})
|
||||
self.assertEqual(trials[0].evaluated_params, {"y": 2, "z": 3})
|
||||
self.assertEqual(trials[0].experiment_tag, "0_y=2,z=3")
|
||||
|
||||
def testDependentLambda(self):
|
||||
trials = self.generate_trials({
|
||||
"run": "PPO",
|
||||
"config": {
|
||||
"x": grid_search([1, 2]),
|
||||
"y": tune.sample_from(lambda spec: spec.config.x * 100),
|
||||
},
|
||||
}, "dependent_lambda")
|
||||
trials = list(trials)
|
||||
self.assertEqual(len(trials), 2)
|
||||
self.assertEqual(trials[0].config, {"x": 1, "y": 100})
|
||||
self.assertEqual(trials[1].config, {"x": 2, "y": 200})
|
||||
|
||||
def testDependentGridSearch(self):
|
||||
trials = self.generate_trials({
|
||||
"run": "PPO",
|
||||
"config": {
|
||||
"x": grid_search([
|
||||
tune.sample_from(lambda spec: spec.config.y * 100),
|
||||
tune.sample_from(lambda spec: spec.config.y * 200)
|
||||
]),
|
||||
"y": tune.sample_from(lambda spec: 1),
|
||||
},
|
||||
}, "dependent_grid_search")
|
||||
trials = list(trials)
|
||||
self.assertEqual(len(trials), 2)
|
||||
self.assertEqual(trials[0].config, {"x": 100, "y": 1})
|
||||
self.assertEqual(trials[1].config, {"x": 200, "y": 1})
|
||||
|
||||
def testNestedValues(self):
|
||||
trials = self.generate_trials({
|
||||
"run": "PPO",
|
||||
"config": {
|
||||
"x": {
|
||||
"y": {
|
||||
"z": tune.sample_from(lambda spec: 1)
|
||||
}
|
||||
},
|
||||
"y": tune.sample_from(lambda spec: 12),
|
||||
"z": tune.sample_from(lambda spec: spec.config.x.y.z * 100),
|
||||
},
|
||||
}, "nested_values")
|
||||
trials = list(trials)
|
||||
self.assertEqual(len(trials), 1)
|
||||
self.assertEqual(trials[0].config, {
|
||||
"x": {
|
||||
"y": {
|
||||
"z": 1
|
||||
}
|
||||
},
|
||||
"y": 12,
|
||||
"z": 100
|
||||
})
|
||||
self.assertEqual(trials[0].evaluated_params, {
|
||||
"x/y/z": 1,
|
||||
"y": 12,
|
||||
"z": 100
|
||||
})
|
||||
|
||||
def testLogUniform(self):
|
||||
sampler = tune.loguniform(1e-10, 1e-1).func
|
||||
results = [sampler(None) for i in range(1000)]
|
||||
assert abs(np.log(min(results)) / np.log(10) - -10) < 0.1
|
||||
assert abs(np.log(max(results)) / np.log(10) - -1) < 0.1
|
||||
|
||||
sampler_e = tune.loguniform(np.e**-4, np.e, base=np.e).func
|
||||
results_e = [sampler_e(None) for i in range(1000)]
|
||||
assert abs(np.log(min(results_e)) - -4) < 0.1
|
||||
assert abs(np.log(max(results_e)) - 1) < 0.1
|
||||
|
||||
def test_resolve_dict(self):
|
||||
config = {
|
||||
"a": {
|
||||
"b": 1,
|
||||
"c": 2,
|
||||
},
|
||||
"b": {
|
||||
"a": 3
|
||||
}
|
||||
}
|
||||
resolved = resolve_nested_dict(config)
|
||||
for k, v in [(("a", "b"), 1), (("a", "c"), 2), (("b", "a"), 3)]:
|
||||
self.assertEqual(resolved.get(k), v)
|
||||
|
||||
def testRecursiveDep(self):
|
||||
try:
|
||||
list(
|
||||
self.generate_trials({
|
||||
"run": "PPO",
|
||||
"config": {
|
||||
"foo": tune.sample_from(lambda spec: spec.config.foo),
|
||||
},
|
||||
}, "recursive_dep"))
|
||||
except RecursiveDependencyError as e:
|
||||
assert "`foo` recursively depends on" in str(e), e
|
||||
else:
|
||||
assert False
|
||||
|
||||
def testMaxConcurrentSuggestions(self):
|
||||
"""Checks that next_trials() supports throttling."""
|
||||
experiment_spec = {
|
||||
"run": "PPO",
|
||||
"num_samples": 6,
|
||||
}
|
||||
experiments = [Experiment.from_json("test", experiment_spec)]
|
||||
|
||||
searcher = _MockSuggestionAlgorithm(max_concurrent=4)
|
||||
searcher.add_configurations(experiments)
|
||||
trials = searcher.next_trials()
|
||||
self.assertEqual(len(trials), 4)
|
||||
self.assertEqual(searcher.next_trials(), [])
|
||||
|
||||
finished_trial = trials.pop()
|
||||
searcher.on_trial_complete(finished_trial.trial_id)
|
||||
self.assertEqual(len(searcher.next_trials()), 1)
|
||||
|
||||
finished_trial = trials.pop()
|
||||
searcher.on_trial_complete(finished_trial.trial_id)
|
||||
|
||||
finished_trial = trials.pop()
|
||||
searcher.on_trial_complete(finished_trial.trial_id)
|
||||
|
||||
finished_trial = trials.pop()
|
||||
searcher.on_trial_complete(finished_trial.trial_id)
|
||||
self.assertEqual(len(searcher.next_trials()), 1)
|
||||
self.assertEqual(len(searcher.next_trials()), 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import pytest
|
||||
import sys
|
||||
sys.exit(pytest.main(["-v", __file__]))
|
||||
Reference in New Issue
Block a user