Skip to content

Commit

Permalink
long live smallab2
Browse files Browse the repository at this point in the history
  • Loading branch information
octopuscabbage committed Dec 10, 2021
1 parent 0be3d49 commit e880315
Show file tree
Hide file tree
Showing 25 changed files with 165 additions and 226 deletions.
4 changes: 4 additions & 0 deletions examples/1_simple_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@


# Write a simple experiment
from smallab.name_helper.dict import dict2name
from smallab.runner.runner import ExperimentRunner


Expand All @@ -24,6 +25,9 @@ def main(self, specification: typing.Dict) -> typing.Dict:
#return the random number. This, along with the specification that generated it will be saved
return {"number": random.random()}

def get_name(self, specification):
return dict2name(specification)

#The name describes what experiment your doing
name = "simple_experiment1"
#The specifications are a list of dictionaries that will get passed to your experiment instance
Expand Down
5 changes: 4 additions & 1 deletion examples/2_simple_experiment_with_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@


#This is another simple experiment, this time with logging involved!
from smallab.name_helper.dict import dict2name
from smallab.runner.runner import ExperimentRunner


Expand All @@ -21,10 +22,12 @@ def main(self, specification: typing.Dict) -> typing.Dict:
logging.getLogger(self.get_logger_name()).info("...")
random.random()
return {"number": random.random()}
def get_name(self,specification):
return dict2name(specification)


name = "simple_experiment2"
runner = ExperimentRunner()
runner.run(name,[{"seed":1,"num_calls":1},{"seed":2,"num_calls":2}],SimpleExperiment())
runner.run(name, [{"seed": 1, "num_calls": 1}, {"seed": 2, "num_calls": 2}], SimpleExperiment(), use_dashboard=False)

delete_experiments_folder(name)
7 changes: 5 additions & 2 deletions examples/3_specification_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from examples.example_utils import delete_experiments_folder
from smallab.experiment_types.experiment import Experiment
from smallab.name_helper.dict import dict2name
from smallab.runner.runner import ExperimentRunner
from smallab.runner_implementations.multiprocessing_runner import MultiprocessingRunner
from smallab.specification_generator import SpecificationGenerator
Expand All @@ -19,6 +20,8 @@ def main(self, specification: typing.Dict) -> typing.Dict:
logging.getLogger(self.get_logger_name()).info("...")
random.random()
return {"number": random.random()}
def get_name(self,specification):
return dict2name(specification)


# In the generation specification keys that have lists as their values will be cross producted with other list valued keys to create many specifications
Expand All @@ -31,6 +34,6 @@ def main(self, specification: typing.Dict) -> typing.Dict:

name = "specification_generation_experiment"
runner = ExperimentRunner()
runner.run(name, specifications, SimpleExperiment(), specification_runner=MultiprocessingRunner(),use_dashboard=False,use_diff_namer=False)
runner.run(name, specifications, SimpleExperiment(), specification_runner=MultiprocessingRunner(), use_dashboard=True)

delete_experiments_folder(name)
delete_experiments_folder(name)
4 changes: 4 additions & 0 deletions examples/4_backend_change.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import random

from smallab.experiment_types.experiment import Experiment
from smallab.name_helper.dict import dict2name
from smallab.runner.runner import ExperimentRunner
from smallab.runner_implementations.multiprocessing_runner import MultiprocessingRunner
from smallab.specification_generator import SpecificationGenerator
Expand All @@ -23,6 +24,9 @@ def main(self, specification: typing.Dict) -> typing.Dict:
time.sleep(10 * r)
return {"number": r}

def get_name(self, specification):
return dict2name(specification)


#Same specification as before
generation_specification = {"seed": [1, 2, 3, 4, 5, 6, 7, 8], "num_calls": [1, 2, 3]}
Expand Down
18 changes: 14 additions & 4 deletions examples/5_checkpointed_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
#Note: Checkpointing does have some overhead so if the experiment is very fast consider not using this
import logging
import random
from copy import deepcopy

from numpy.random.mtrand import RandomState

from examples.example_utils import delete_experiments_folder
from smallab.experiment_types.checkpointed_experiment import CheckpointedExperiment
from smallab.name_helper.dict import dict2name
from smallab.runner.runner import ExperimentRunner
from smallab.runner_implementations.multiprocessing_runner import MultiprocessingRunner
from smallab.smallab_types import Specification
Expand Down Expand Up @@ -35,8 +37,8 @@ def step(self):
# time.sleep(int(.5 * self.r))
#this experiment can have a random transient failure!
#Since it's checkpointed, it will likely succeed after running it again
if random.randint(0,100) > 90:
raise Exception("Something bad happened, a moth flew into the computer!")
#if random.randint(0,100) > 90:
#raise Exception("Something bad happened, a moth flew into the computer!")
if self.i >= self.num_calls:
#Done with the experiment, return the results dictionary like normal
return {"number": self.r}
Expand All @@ -48,6 +50,14 @@ def step(self):
def max_iterations(self, specification):
return specification["num_calls"]

def get_name(self, specification):
return dict2name(specification)

def get_current_name(self, specification):
specification_with_idx = deepcopy(specification)
specification_with_idx["idx"] = self.i
return dict2name(specification_with_idx)


#Same specification as before
generation_specification = {"seed": [1, 2, 3, 4, 5, 6, 7, 8], "num_calls": [100, 200, 300]}
Expand All @@ -56,10 +66,10 @@ def max_iterations(self, specification):
name = "checkpointed_run"
#This time we will run them all in parallel
runner = ExperimentRunner()
runner.run(name, specifications, SimpleExperiment(),specification_runner=MultiprocessingRunner())
runner.run(name, specifications, SimpleExperiment(),use_dashboard=False)

#Some of our experiments may have failed, let's call run again to hopefully solve that
runner.run(name, specifications, SimpleExperiment(),specification_runner=MultiprocessingRunner())

#Cleanup example
delete_experiments_folder(name)
delete_experiments_folder(name)
33 changes: 18 additions & 15 deletions examples/6_overlapping_checkpointed_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from examples.example_utils import delete_experiments_folder
from smallab.experiment_types.overlapping_output_experiment import OverlappingOutputCheckpointedExperiment, \
OverlappingOutputCheckpointedExperimentReturnValue
from smallab.name_helper.dict import dict2name
from smallab.runner.runner import ExperimentRunner
from smallab.runner_implementations.multiprocessing_runner import MultiprocessingRunner
from smallab.smallab_types import Specification
Expand Down Expand Up @@ -35,35 +36,37 @@ def step(self):
# Since it's checkpointed, it will likely succeed after running it again
# if random.randint(0,100) > 90:
# raise Exception("Something bad happened, a moth flew into the computer!")
if self.i in self.num_calls:
should_continue = self.i != self.num_calls[-1]
specification = deepcopy(self.specification)
specification["num_calls"] = self.i
result = {"r": self.r}
progress = self.i
max_iterations = self.num_calls
return OverlappingOutputCheckpointedExperimentReturnValue(should_continue, specification, result, progress,
max_iterations)
else:
# This experiment isn't done, return the progress as a tuple to update the dashboard
return (self.i, self.num_calls)
should_continue = self.i != self.num_calls
specification = deepcopy(self.specification)
specification["num_calls"] = self.i
result = {"r": self.r}
progress = self.i
max_iterations = self.num_calls
return OverlappingOutputCheckpointedExperimentReturnValue(should_continue, specification, result, progress,
max_iterations)
#Tells the dashboard how many iterations this experiment will run for
def max_iterations(self,specification):
return specification["num_calls"]

def get_name(self,specification):
return dict2name(specification)
def get_current_name(self,specification):
cur_specification = deepcopy(specification)
cur_specification["idx"] = self.i
return dict2name(cur_specification)

# Same specification as before
generation_specification = {"seed": [1, 2, 3, 4, 5, 6, 7, 8], "num_calls": (10, 20, 30)}
generation_specification = {"seed": [1, 2, 3, 4, 5, 6, 7, 8], "num_calls": 30}
specifications = SpecificationGenerator().generate(generation_specification)

name = "overlapping_checkpointed_run"
# This time we will run them all in parallel
runner = ExperimentRunner()
runner.run(name, specifications, SimpleExperiment(), specification_runner=MultiprocessingRunner(), use_dashboard=False,
runner.run(name, specifications, SimpleExperiment(), specification_runner=MultiprocessingRunner(), use_dashboard=True,
propagate_exceptions=True)

# Some of our experiments may have failed, let's call run again to hopefully solve that
runner.run(name, specifications, SimpleExperiment(), specification_runner=MultiprocessingRunner(), use_dashboard=False,
runner.run(name, specifications, SimpleExperiment(), specification_runner=MultiprocessingRunner(), use_dashboard=True,
propagate_exceptions=True)

# Cleanup example
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def read(fname):

setup(
name="smallab",
version="1.12.0",
version="2.0.0",
url='https://github.com/octopuscabbage/smallab',
packages=find_packages(),
install_requires=required,
Expand Down
43 changes: 21 additions & 22 deletions smallab/dashboard/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ def draw_header_widget(row, stdscr, experiment_name, width, complete, active, re


def draw_specifications_widget(row, stdscr, active, registered, width, specification_progress, height, failed,
specification_id_to_specification, specification_readout_index, use_diff_namer):
specification_id_to_specification, specification_readout_index):
start_row = row
# Decide to draw in single or double column
second_column_begins = math.floor(width / 2)
Expand Down Expand Up @@ -429,25 +429,24 @@ def draw_specifications_widget(row, stdscr, active, registered, width, specifica
stdscr.addstr(row, width - len(status_string), status_string)
specification_readout_end_index = width - len(status_string)

if not use_diff_namer:
specification = str(specification_id_to_specification[active_specification])
specification_string_start_index = specification_readout_index % len(specification)
max_allowed_length = specification_readout_end_index - specification_readout_start_index - 1
if len(specification) <= max_allowed_length:
specification = str(specification_id_to_specification[active_specification])
specification_string_start_index = specification_readout_index % len(specification)
max_allowed_length = specification_readout_end_index - specification_readout_start_index - 1
if len(specification) <= max_allowed_length:
stdscr.addstr(row, specification_readout_start_index,
specification)
else:
overflow = specification_string_start_index + max_allowed_length - len(specification) - 1
if overflow > 0:
stdscr.addstr(row, specification_readout_start_index,
specification)
else:
overflow = specification_string_start_index + max_allowed_length - len(specification) - 1
if overflow > 0:
stdscr.addstr(row, specification_readout_start_index,
specification[
specification_string_start_index:specification_string_start_index + max_allowed_length] + " " + specification[
:overflow])
specification[
specification_string_start_index:specification_string_start_index + max_allowed_length] + " " + specification[
:overflow])

else:
stdscr.addstr(row, specification_readout_start_index,
specification[
specification_string_start_index:specification_string_start_index + max_allowed_length])
else:
stdscr.addstr(row, specification_readout_start_index,
specification[
specification_string_start_index:specification_string_start_index + max_allowed_length])
row += 1
if row >= max_height:
if use_double_column_layout and not on_second_column:
Expand Down Expand Up @@ -478,7 +477,7 @@ def draw_log_widget(row, stdscr, width, height, log_spool):
return row


def run(stdscr, eventQueue, name,use_diff_namer):
def run(stdscr, eventQueue, name):
specification_ids_to_specification = dict()
max_events_per_frame = 1000
max_log_spool_events = 10**3
Expand Down Expand Up @@ -543,7 +542,7 @@ def run(stdscr, eventQueue, name,use_diff_namer):
row = draw_header_widget(row, stdscr, experiment_name, width, complete, active, registered,
specification_progress, timeestimator, failed, in_slow_mode=in_slow_mode)
row = draw_specifications_widget(row, stdscr, active, registered, width, specification_progress, height,
failed, specification_ids_to_specification, specification_readout_index,use_diff_namer)
failed, specification_ids_to_specification, specification_readout_index)
row = draw_log_widget(row, stdscr, width, height, log_spool)
stdscr.refresh()
time.sleep(0.1)
Expand All @@ -552,6 +551,6 @@ def run(stdscr, eventQueue, name,use_diff_namer):
logging.getLogger("smallab.dashboard").error("Dashboard Error {}".format(e), exc_info=True)


def start_dashboard(eventQueue, name, use_diff_namer):
curses.wrapper(run, eventQueue, name,use_diff_namer)
def start_dashboard(eventQueue, name):
curses.wrapper(run, eventQueue, name)

56 changes: 0 additions & 56 deletions smallab/experiment_naming.py

This file was deleted.

4 changes: 4 additions & 0 deletions smallab/experiment_types/checkpointed_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ class IterativeExperiment(abc.ABC):
def max_iterations(self,specification):
pass

@abc.abstractmethod
def get_current_name(self, specification):
pass

class CheckpointedExperiment(ExperimentBase, HasCheckpoint, IterativeExperiment):
"""
CheckpointedExperiment is an Experiment which can be stopped and restarted.
Expand Down
5 changes: 5 additions & 0 deletions smallab/experiment_types/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@


class ExperimentBase(abc.ABC):

@abc.abstractmethod
def get_name(self, specification):
pass

def set_experiment_local_storage(self,experiment_local_storage_folder):
"""
Called by ExperimentRunner to set up a folder for saving temporary data to file during an experiment, such as model weights
Expand Down
Loading

0 comments on commit e880315

Please sign in to comment.