diff --git a/.gitignore b/.gitignore index d8a17c5..90323d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,87 +1,87 @@ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class .pytest_cache/ - +*.vpp.bak_* python_tests_xml # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg .idea/ # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover .hypothesis/ # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ doc/ # PyBuilder target/ #Ipython Notebook .ipynb_checkpoints #Pycharm files *.iml # merging stuff *.orig *~ # Paths in repository mcml.py # images etc *.tif *.nrrd *.caffemodel # C++ stuff build* *.user diff --git a/bin/hyppopy_exe.py b/bin/hyppopy_exe.py index a69d792..887f71a 100644 --- a/bin/hyppopy_exe.py +++ b/bin/hyppopy_exe.py @@ -1,23 +1,24 @@ # -*- coding: utf-8 -*- # # DKFZ # # # Copyright (c) German Cancer Research Center, # Division of Medical and Biological Informatics. # All rights reserved. # # This software is distributed WITHOUT ANY WARRANTY; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. # # See LICENSE.txt or http://www.mitk.org for details. # # Author: Sven Wanner (s.wanner@dkfz.de) from hyppopy.cmdtools import * if __name__ == '__main__': - cmd_workflow() + #cmd_workflow() + pass diff --git a/hyppopy/cmdtools.py b/hyppopy/cmdtools.py deleted file mode 100644 index 15c0184..0000000 --- a/hyppopy/cmdtools.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DKFZ -# -# -# Copyright (c) German Cancer Research Center, -# Division of Medical and Biological Informatics. -# All rights reserved. -# -# This software is distributed WITHOUT ANY WARRANTY; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. -# -# See LICENSE.txt or http://www.mitk.org for details. -# -# Author: Sven Wanner (s.wanner@dkfz.de) - -import argparse - -import logging -LOG = logging.getLogger('hyppopy') - -from hyppopy.solver_factory import SolverFactory - - -def cmd_workflow(): - parser = argparse.ArgumentParser(description="") - - parser.add_argument('-v', '--verbosity', type=int, required=False, default=0, - help='number of thoughts our thinker should produce') - - - args_dict = vars(parser.parse_args()) diff --git a/hyppopy/helpers.py b/hyppopy/helpers.py deleted file mode 100644 index 8d9eca9..0000000 --- a/hyppopy/helpers.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DKFZ -# -# -# Copyright (c) German Cancer Research Center, -# Division of Medical and Biological Informatics. -# All rights reserved. -# -# This software is distributed WITHOUT ANY WARRANTY; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. -# -# See LICENSE.txt or http://www.mitk.org for details. -# -# Author: Sven Wanner (s.wanner@dkfz.de) diff --git a/hyppopy/iparameterspace.py b/hyppopy/iparameterspace.py deleted file mode 100644 index c29987b..0000000 --- a/hyppopy/iparameterspace.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DKFZ -# -# -# Copyright (c) German Cancer Research Center, -# Division of Medical and Biological Informatics. -# All rights reserved. -# -# This software is distributed WITHOUT ANY WARRANTY; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. -# -# See LICENSE.txt or http://www.mitk.org for details. -# -# Author: Sven Wanner (s.wanner@dkfz.de) - -from hyppopy.deepdict.deepdict import DeepDict - -import abc -import logging -LOG = logging.getLogger('hyppopy') - - -class IParameterSpace(DeepDict, metaclass=abc.ABCMeta): - - def __init__(self, in_data=None): - DeepDict.__init__(self, in_data=in_data, path_sep='/') - - def status(self): - return "ok" - - @abc.abstractmethod - def convert(self): - raise NotImplementedError('users must define convert to use this base class') diff --git a/hyppopy/isolver.py b/hyppopy/isolver.py deleted file mode 100644 index c16b0e5..0000000 --- a/hyppopy/isolver.py +++ /dev/null @@ -1,45 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DKFZ -# -# -# Copyright (c) German Cancer Research Center, -# Division of Medical and Biological Informatics. -# All rights reserved. -# -# This software is distributed WITHOUT ANY WARRANTY; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. -# -# See LICENSE.txt or http://www.mitk.org for details. -# -# Author: Sven Wanner (s.wanner@dkfz.de) - -import abc -import logging -LOG = logging.getLogger('hyppopy') - - -class ISolver(object, metaclass=abc.ABCMeta): - loss_function = None - space = None - - def set_loss_function(self, func): - """ - set loss function - """ - self.loss_function = func - - def set_space(self, space): - """ - set loss function - """ - self.space = space - - def status(self): - return "ok" - - @abc.abstractmethod - def execute(self, *args, **kwargs): - raise NotImplementedError('users must define execute to use this base class') - diff --git a/hyppopy/plugins/hyperopt_solver_plugin.py b/hyppopy/plugins/hyperopt_solver_plugin.py new file mode 100644 index 0000000..3a20be1 --- /dev/null +++ b/hyppopy/plugins/hyperopt_solver_plugin.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# +# DKFZ +# +# +# Copyright (c) German Cancer Research Center, +# Division of Medical and Biological Informatics. +# All rights reserved. +# +# This software is distributed WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. +# +# See LICENSE.txt or http://www.mitk.org for details. +# +# Author: Sven Wanner (s.wanner@dkfz.de) + +import logging +LOG = logging.getLogger('hyppopy') +from pprint import pformat + +try: + from hyperopt import fmin, tpe, hp, STATUS_OK, STATUS_FAIL, Trials + from yapsy.IPlugin import IPlugin +except: + LOG.warning("Hyperopt package not installed, will ignore this plugin!") + print("Hyperopt package not installed, will ignore this plugin!") + +from hyppopy.solverpluginbase import SolverPluginBase + + +class hyperopt_Solver(SolverPluginBase, IPlugin): + trials = None + best = None + + def __init__(self): + LOG.debug("hyperopt_Solver.__init__()") + SolverPluginBase.__init__(self) + + def loss_function(self, params): + try: + loss = self.loss(self.data, params) + status = STATUS_OK + except Exception as e: + status = STATUS_FAIL + return {'loss': loss, 'status': status} + + def convert_parameter(self, params): + LOG.debug(f"convert_parameter({params})") + + self.solution_space = {} + for name, content in params.items(): + data = None + domain = None + domain_fn = None + for key, value in content.items(): + if key == 'domain': + domain = value + if value == 'uniform': + domain_fn = hp.uniform + if value == 'categorical': + domain_fn = hp.choice + if key == 'data': + data = value + if domain == 'categorical': + self.solution_space[name] = domain_fn(name, data) + else: + self.solution_space[name] = domain_fn(name, data[0], data[1]) + + def execute_solver(self): + LOG.debug(f"execute_solver using solution space -> {pformat(self.solution_space)}") + self.trials = Trials() + try: + self.best = fmin(fn=self.loss_function, + space=self.solution_space, + algo=tpe.suggest, + max_evals=50, + trials=self.trials) + except Exception as e: + LOG.error(f"Internal error in hyperopt.fmin occured. {e}") + raise BrokenPipeError(f"Internal error in hyperopt.fmin occured. {e}") + + def convert_results(self): + solution = dict([(k, v) for k, v in self.best.items() if v is not None]) + print('Solution\n========') + print("\n".join(map(lambda x: "%s \t %s" % (x[0], str(x[1])), solution.items()))) diff --git a/hyppopy/solver_plugins/hyperopt_solver_plugin.yapsy-plugin b/hyppopy/plugins/hyperopt_solver_plugin.yapsy-plugin similarity index 100% rename from hyppopy/solver_plugins/hyperopt_solver_plugin.yapsy-plugin rename to hyppopy/plugins/hyperopt_solver_plugin.yapsy-plugin diff --git a/hyppopy/plugins/optunity_solver_plugin.py b/hyppopy/plugins/optunity_solver_plugin.py new file mode 100644 index 0000000..75d9ab3 --- /dev/null +++ b/hyppopy/plugins/optunity_solver_plugin.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +# +# DKFZ +# +# +# Copyright (c) German Cancer Research Center, +# Division of Medical and Biological Informatics. +# All rights reserved. +# +# This software is distributed WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. +# +# See LICENSE.txt or http://www.mitk.org for details. +# +# Author: Sven Wanner (s.wanner@dkfz.de) + +import logging +LOG = logging.getLogger('hyppopy') +from pprint import pformat + +try: + import optunity + from yapsy.IPlugin import IPlugin +except: + LOG.warning("Optunity package not installed, will ignore this plugin!") + print("Optunity package not installed, will ignore this plugin!") + +from hyppopy.solverpluginbase import SolverPluginBase + + +class optunity_Solver(SolverPluginBase, IPlugin): + solver_info = None + trials = None + best = None + status = None + + def __init__(self): + LOG.debug("optunity_Solver.__init__()") + SolverPluginBase.__init__(self) + + def loss_function(self, **params): + try: + loss = self.loss(self.data, params) + self.status.append('ok') + return loss + except Exception as e: + self.status.append('fail') + return 1e9 + + def convert_parameter(self, params): + LOG.debug(f"convert_parameter({params})") + + # define function spliting input dict + # into categorical and non-categorical + def split_categorical(pdict): + categorical = {} + uniform = {} + for name, pset in pdict.items(): + for key, value in pset.items(): + if key == 'domain' and value == 'categorical': + categorical[name] = pset + elif key == 'domain': + uniform[name] = pset + return categorical, uniform + + self.solution_space = {} + # split input in categorical and non-categorical data + cat, uni = split_categorical(params) + # build up dictionary keeping all non-categorical data + uniforms = {} + for key, value in uni.items(): + for key2, value2 in value.items(): + if key2 == 'data': + uniforms[key] = value2 + + # build nested categorical structure + inner_level = uniforms + for key, value in cat.items(): + tmp = {} + tmp2 = {} + for key2, value2 in value.items(): + if key2 == 'data': + for elem in value2: + tmp[elem] = inner_level + tmp2[key] = tmp + inner_level = tmp2 + self.solution_space = tmp2 + + def execute_solver(self): + LOG.debug(f"execute_solver using solution space -> {pformat(self.solution_space)}") + self.status = [] + try: + self.best, self.trials, self.solver_info = optunity.minimize_structured(f=self.loss_function, + num_evals=50, + search_space=self.solution_space) + except Exception as e: + LOG.error(f"Internal error in optunity.minimize_structured occured. {e}") + raise BrokenPipeError(f"Internal error in optunity.minimize_structured occured. {e}") + + def convert_results(self): + solution = dict([(k, v) for k, v in self.best.items() if v is not None]) + print('Solution\n========') + print("\n".join(map(lambda x: "%s \t %s" % (x[0], str(x[1])), solution.items()))) + print(f"Solver used: {self.solver_info['solver_name']}") + print(f"Optimum: {self.trials.optimum}") + print(f"Iterations used: {self.trials.stats['num_evals']}") + print(f"Duration: {self.trials.stats['time']} s") diff --git a/hyppopy/solver_plugins/optunity_solver_plugin.yapsy-plugin b/hyppopy/plugins/optunity_solver_plugin.yapsy-plugin similarity index 100% rename from hyppopy/solver_plugins/optunity_solver_plugin.yapsy-plugin rename to hyppopy/plugins/optunity_solver_plugin.yapsy-plugin diff --git a/hyppopy/settings.py b/hyppopy/settings.py index 9da8907..dd68235 100644 --- a/hyppopy/settings.py +++ b/hyppopy/settings.py @@ -1,33 +1,25 @@ # DKFZ # # Copyright (c) German Cancer Research Center, # Division of Medical and Biological Informatics. # All rights reserved. # # This software is distributed WITHOUT ANY WARRANTY; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. # # See LICENSE.txt or http://www.mitk.org for details. # -*- coding: utf-8 -*- import os import sys import logging ROOT = os.path.join(os.path.dirname(__file__), "..") LOGFILENAME = os.path.join(ROOT, 'logfile.log') -PLUGIN_DEFAULT_DIR = os.path.join(ROOT, *("hyppopy", "solver_plugins")) +PLUGIN_DEFAULT_DIR = os.path.join(ROOT, *("hyppopy", "plugins")) sys.path.insert(0, ROOT) logging.getLogger('hyppopy').setLevel(logging.DEBUG) logging.basicConfig(filename=LOGFILENAME, filemode='w', format='%(name)s - %(levelname)s - %(message)s') - -''' -LOG.debug('debug message') -LOG.info('info message') -LOG.warning('warning message') -LOG.error('error message') -LOG.critical('critical message') -''' diff --git a/hyppopy/solver.py b/hyppopy/solver.py index cae16e1..2edac91 100644 --- a/hyppopy/solver.py +++ b/hyppopy/solver.py @@ -1,70 +1,75 @@ # -*- coding: utf-8 -*- # # DKFZ # # # Copyright (c) German Cancer Research Center, # Division of Medical and Biological Informatics. # All rights reserved. # # This software is distributed WITHOUT ANY WARRANTY; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. # # See LICENSE.txt or http://www.mitk.org for details. # # Author: Sven Wanner (s.wanner@dkfz.de) import logging LOG = logging.getLogger('hyppopy') class Solver(object): _name = None - _solver = None - _parameter = None - - def __init__(self, name=None): - self.set_name(name) - - def __str__(self): - txt = f"\nSolver Instance {self._name}:" - if self._solver is None: - txt += f"\n - Status solver: None" - else: - txt += f"\n - Status solver: ok" - if self._parameter is None: - txt += f"\n - Status parameter: None" - else: - txt += f"\n - Status parameter: ok" - return txt + _solver_plugin = None + _settings_plugin = None - def is_ready(self): - return self._solver is not None and self._parameter is not None + def __init__(self): + pass + + def set_data(self, data): + self.solver.set_data(data) + + def set_parameters(self, params): + self.solver.set_parameters(params) + + def set_loss_function(self, loss_func): + self.solver.set_loss_function(loss_func) + + def run(self): + self.solver.run() + + def get_results(self): + self.solver.get_results() - def set_name(self, name): - LOG.debug(f"set_name({name})") - self._name = name + @property + def is_ready(self): + return self.solver is not None and self.settings is not None @property def solver(self): - return self._solver + return self._solver_plugin @solver.setter def solver(self, value): - if not type(value).__name__.endswith("Solver"): - LOG.error("Input Error, value is not of type Solver") - raise IOError("Input Error, value is not of type Solver") - self._solver = value + self._solver_plugin = value + + @property + def settings(self): + return self._settings_plugin + + @settings.setter + def settings(self, value): + self._settings_plugin = value @property - def parameter(self): - return self._parameter - - @parameter.setter - def parameter(self, value): - if not type(value).__name__.endswith("ParameterSpace"): - LOG.error("Input Error, value is not of type ParameterSpace") - raise IOError("Input Error, value is not of type ParameterSpace") - self._parameter = value + def name(self): + return self._name + + @name.setter + def name(self, value): + if not isinstance(value, str): + LOG.error(f"Invalid input, str type expected for value, got {type(value)} instead") + raise IOError(f"Invalid input, str type expected for value, got {type(value)} instead") + self._name = value diff --git a/hyppopy/solver_factory.py b/hyppopy/solver_factory.py deleted file mode 100644 index 38c8d25..0000000 --- a/hyppopy/solver_factory.py +++ /dev/null @@ -1,99 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DKFZ -# -# -# Copyright (c) German Cancer Research Center, -# Division of Medical and Biological Informatics. -# All rights reserved. -# -# This software is distributed WITHOUT ANY WARRANTY; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. -# -# See LICENSE.txt or http://www.mitk.org for details. -# -# Author: Sven Wanner (s.wanner@dkfz.de) - -from yapsy.PluginManager import PluginManager - -from .settings import PLUGIN_DEFAULT_DIR -from .solver import Solver - -import logging -LOG = logging.getLogger('hyppopy') - - -class SolverFactory(object): - _instance = None - _plugin_dirs = [] - _plugins = {} - - def __init__(self): - if SolverFactory._instance is not None: - pass - else: - LOG.debug("__init__()") - SolverFactory._instance = self - self.reset() - self.load_plugins() - - @staticmethod - def instance(): - """ - Singleton instance access - :return: [SolverFactory] instance - """ - LOG.debug("instance()") - if SolverFactory._instance is None: - SolverFactory() - return SolverFactory._instance - - def get_solver_names(self): - return list(self._plugins.keys()) - - def get_solver(self, name, **kwargs): - if name not in self._plugins.keys(): - LOG.error(f"Solver plugin {name} not available") - raise KeyError(f"Solver plugin {name} not available") - return self._plugins[name] - - def add_plugin_dir(self, dir): - """ - Add plugin directory - """ - LOG.debug(f"add_plugin_dir({dir})") - self._plugin_dirs.append(dir) - - def reset(self): - """ - Reset solver factory - """ - LOG.debug("reset()") - self._plugins = {} - self._plugin_dirs = [] - self.add_plugin_dir(PLUGIN_DEFAULT_DIR) - - def load_plugins(self): - """ - Load plugin modules from plugin paths - """ - LOG.debug("load_plugins()") - LOG.debug(f"setPluginPlaces(" + " ".join(map(str, self._plugin_dirs))) - manager = PluginManager() - manager.setPluginPlaces(self._plugin_dirs) - manager.collectPlugins() - for plugin in manager.getAllPlugins(): - name, type = plugin.plugin_object.__class__.__name__.split("_") - if name not in self._plugins.keys(): - self._plugins[name] = Solver(name) - if type == "Solver": - self._plugins[name].solver = plugin.plugin_object.__class__() - LOG.info(f"Plugin: {name} Solver loaded") - elif type == "ParameterSpace": - self._plugins[name].parameter = plugin.plugin_object.__class__() - LOG.info(f"Plugin: {name} ParameterSpace loaded") - else: - LOG.error(f"Failed loading plugin {name}! Please check if naming conventions are kept!") - raise IOError(f"Failed loading plugin {name}! Please check if naming conventions are kept!") - diff --git a/hyppopy/solver_plugins/__init__.py b/hyppopy/solver_plugins/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/hyppopy/solver_plugins/hyperopt_parameterspace_plugin.py b/hyppopy/solver_plugins/hyperopt_parameterspace_plugin.py deleted file mode 100644 index 7dc9dc8..0000000 --- a/hyppopy/solver_plugins/hyperopt_parameterspace_plugin.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DKFZ -# -# -# Copyright (c) German Cancer Research Center, -# Division of Medical and Biological Informatics. -# All rights reserved. -# -# This software is distributed WITHOUT ANY WARRANTY; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. -# -# See LICENSE.txt or http://www.mitk.org for details. -# -# Author: Sven Wanner (s.wanner@dkfz.de) - -from yapsy.IPlugin import IPlugin - -from hyppopy.isolver import ISolver -from hyppopy.iparameterspace import IParameterSpace - -import logging -LOG = logging.getLogger('hyppopy') - - -class hyperopt_ParameterSpace(IPlugin, IParameterSpace): - - def convert(self): - """ - Converts the internal IParameterSpace data into the form preferred by - the Hyperopt package. Hyperopt has it's own parameter space definition - for each type of hyperparameter: - - https://github.com/hyperopt/hyperopt/wiki/FMin#21-parameter-expressions - :return: - """ - pass - diff --git a/hyppopy/solver_plugins/hyperopt_parameterspace_plugin.yapsy-plugin b/hyppopy/solver_plugins/hyperopt_parameterspace_plugin.yapsy-plugin deleted file mode 100644 index 3325f99..0000000 --- a/hyppopy/solver_plugins/hyperopt_parameterspace_plugin.yapsy-plugin +++ /dev/null @@ -1,9 +0,0 @@ -[Core] -Name = hyperopt -Module = hyperopt_parameterspace_plugin - -[Documentation] -Author = Sven Wanner -Version = 0.1 -Website = https://github.com/hyperopt/hyperopt -Description = Hyperopt ParameterSpace Plugin \ No newline at end of file diff --git a/hyppopy/solver_plugins/hyperopt_solver_plugin.py b/hyppopy/solver_plugins/hyperopt_solver_plugin.py deleted file mode 100644 index 4814d0b..0000000 --- a/hyppopy/solver_plugins/hyperopt_solver_plugin.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DKFZ -# -# -# Copyright (c) German Cancer Research Center, -# Division of Medical and Biological Informatics. -# All rights reserved. -# -# This software is distributed WITHOUT ANY WARRANTY; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. -# -# See LICENSE.txt or http://www.mitk.org for details. -# -# Author: Sven Wanner (s.wanner@dkfz.de) - -from yapsy.IPlugin import IPlugin - -from hyppopy.isolver import ISolver -from hyppopy.iparameterspace import IParameterSpace - -import logging -LOG = logging.getLogger('hyppopy') - - -class hyperopt_Solver(IPlugin, ISolver): - - def execute(self, *args, **kwargs): - pass - diff --git a/hyppopy/solver_plugins/optunity_parameterspace_plugin.py b/hyppopy/solver_plugins/optunity_parameterspace_plugin.py deleted file mode 100644 index 49d1b61..0000000 --- a/hyppopy/solver_plugins/optunity_parameterspace_plugin.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DKFZ -# -# -# Copyright (c) German Cancer Research Center, -# Division of Medical and Biological Informatics. -# All rights reserved. -# -# This software is distributed WITHOUT ANY WARRANTY; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. -# -# See LICENSE.txt or http://www.mitk.org for details. -# -# Author: Sven Wanner (s.wanner@dkfz.de) - -from yapsy.IPlugin import IPlugin - -from hyppopy.isolver import ISolver -from hyppopy.iparameterspace import IParameterSpace - -import logging -LOG = logging.getLogger('hyppopy') - - -class optunity_ParameterSpace(IPlugin, IParameterSpace): - - def convert(self): - pass diff --git a/hyppopy/solver_plugins/optunity_parameterspace_plugin.yapsy-plugin b/hyppopy/solver_plugins/optunity_parameterspace_plugin.yapsy-plugin deleted file mode 100644 index 4d31e1c..0000000 --- a/hyppopy/solver_plugins/optunity_parameterspace_plugin.yapsy-plugin +++ /dev/null @@ -1,9 +0,0 @@ -[Core] -Name = optunity -Module = optunity_parameterspace_plugin - -[Documentation] -Author = Sven Wanner -Version = 0.1 -Website = https://optunity.readthedocs.io/en/latest/ -Description = Optunity ParameterSpace Plugin \ No newline at end of file diff --git a/hyppopy/solver_plugins/optunity_solver_plugin.py b/hyppopy/solver_plugins/optunity_solver_plugin.py deleted file mode 100644 index df6a64e..0000000 --- a/hyppopy/solver_plugins/optunity_solver_plugin.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DKFZ -# -# -# Copyright (c) German Cancer Research Center, -# Division of Medical and Biological Informatics. -# All rights reserved. -# -# This software is distributed WITHOUT ANY WARRANTY; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. -# -# See LICENSE.txt or http://www.mitk.org for details. -# -# Author: Sven Wanner (s.wanner@dkfz.de) - -from yapsy.IPlugin import IPlugin - -from hyppopy.isolver import ISolver -from hyppopy.iparameterspace import IParameterSpace - -import logging -LOG = logging.getLogger('hyppopy') - - -class optunity_Solver(IPlugin, ISolver): - - def execute(self, *args, **kwargs): - pass diff --git a/hyppopy/solverfactory.py b/hyppopy/solverfactory.py new file mode 100644 index 0000000..218b1a9 --- /dev/null +++ b/hyppopy/solverfactory.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +# +# DKFZ +# +# +# Copyright (c) German Cancer Research Center, +# Division of Medical and Biological Informatics. +# All rights reserved. +# +# This software is distributed WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. +# +# See LICENSE.txt or http://www.mitk.org for details. +# +# Author: Sven Wanner (s.wanner@dkfz.de) + +from yapsy.PluginManager import PluginManager + +from hyppopy.settings import PLUGIN_DEFAULT_DIR +from hyppopy.solver import Solver + +import os +import logging +LOG = logging.getLogger('hyppopy') + + +class SolverFactory(object): + _instance = None + _plugin_dirs = [] + _plugins = {} + + def __init__(self): + if SolverFactory._instance is not None: + pass + else: + LOG.debug("__init__()") + SolverFactory._instance = self + self.reset() + self.load_plugins() + + @staticmethod + def instance(): + """ + Singleton instance access + :return: [SolverFactory] instance + """ + LOG.debug("instance()") + if SolverFactory._instance is None: + SolverFactory() + return SolverFactory._instance + + def load_plugins(self): + """ + Load plugin modules from plugin paths + """ + LOG.debug("load_plugins()") + LOG.debug(f"setPluginPlaces(" + " ".join(map(str, self._plugin_dirs))) + manager = PluginManager() + manager.setPluginPlaces(self._plugin_dirs) + manager.collectPlugins() + for plugin in manager.getAllPlugins(): + name_elements = plugin.plugin_object.__class__.__name__.split("_") + if len(name_elements) != 2 or ("Solver" not in name_elements and "Settings" not in name_elements): + LOG.error(f"Invalid plugin class naming for class {plugin.plugin_object.__class__.__name__}, the convention is libname_Solver or libname_Settings.") + raise NameError(f"Invalid plugin class naming for class {plugin.plugin_object.__class__.__name__}, the convention is libname_Solver or libname_Settings.") + if name_elements[0] not in self._plugins.keys(): + self._plugins[name_elements[0]] = Solver() + self._plugins[name_elements[0]].name = name_elements[0] + if name_elements[1] == "Solver": + try: + self._plugins[name_elements[0]].solver = plugin.plugin_object.__class__() + LOG.info(f"Plugin: {name_elements[0]} Solver loaded") + except Exception as e: + LOG.error(f"Failed to instanciate class {plugin.plugin_object.__class__.__name__}") + raise ImportError(f"Failed to instanciate class {plugin.plugin_object.__class__.__name__}") + elif type == "Settings": + try: + self._plugins[name_elements[0]].settings = plugin.plugin_object.__class__() + LOG.info(f"Plugin: {name_elements[0]} ParameterSpace loaded") + except Exception as e: + LOG.error(f"Failed to instanciate class {plugin.plugin_object.__class__.__name__}") + raise ImportError(f"Failed to instanciate class {plugin.plugin_object.__class__.__name__}") + else: + LOG.error(f"Failed loading plugin {name_elements[0]}! Please check if naming conventions are kept!") + raise IOError(f"Failed loading plugin {name_elements[0]}! Please check if naming conventions are kept!") + + def reset(self): + """ + Reset solver factory + """ + LOG.debug("reset()") + self._plugins = {} + self._plugin_dirs = [] + self.add_plugin_dir(os.path.abspath(PLUGIN_DEFAULT_DIR)) + + def add_plugin_dir(self, dir): + """ + Add plugin directory + """ + LOG.debug(f"add_plugin_dir({dir})") + self._plugin_dirs.append(dir) + + def get_solver(self, name): + LOG.debug(f"get_solver({name})") + return self._plugins[name] diff --git a/hyppopy/solverpluginbase.py b/hyppopy/solverpluginbase.py new file mode 100644 index 0000000..7a8c407 --- /dev/null +++ b/hyppopy/solverpluginbase.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +# +# DKFZ +# +# +# Copyright (c) German Cancer Research Center, +# Division of Medical and Biological Informatics. +# All rights reserved. +# +# This software is distributed WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. +# +# See LICENSE.txt or http://www.mitk.org for details. +# +# Author: Sven Wanner (s.wanner@dkfz.de) + +import abc + +import logging +LOG = logging.getLogger('hyppopy') + + +class SolverPluginBase(object): + data = None + loss = None + solution_space = None + _name = None + + def __init__(self): + pass + + @abc.abstractmethod + def loss_function(self, params): + raise NotImplementedError('users must define loss_func to use this base class') + + @abc.abstractmethod + def convert_parameter(self, params): + raise NotImplementedError('users must define convert_parameter to use this base class') + + @abc.abstractmethod + def execute_solver(self): + raise NotImplementedError('users must define execute_solver to use this base class') + + @abc.abstractmethod + def convert_results(self): + raise NotImplementedError('users must define convert_results to use this base class') + + def set_data(self, data): + self.data = data + + def set_parameters(self, params): + self.convert_parameter(params=params) + + def set_loss_function(self, func): + self.loss = func + + def get_results(self): + self.convert_results() + + def run(self): + self.execute_solver() + + @property + def name(self): + return self._name + + @name.setter + def name(self, value): + if not isinstance(value, str): + LOG.error(f"Invalid input, str type expected for value, got {type(value)} instead") + raise IOError(f"Invalid input, str type expected for value, got {type(value)} instead") + self._name = value + diff --git a/hyppopy/tests/test_helpers.py b/hyppopy/tests/test_helpers.py deleted file mode 100644 index 364f6ca..0000000 --- a/hyppopy/tests/test_helpers.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# -# DKFZ -# -# -# Copyright (c) German Cancer Research Center, -# Division of Medical and Biological Informatics. -# All rights reserved. -# -# This software is distributed WITHOUT ANY WARRANTY; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. -# -# See LICENSE.txt or http://www.mitk.org for details. -# -# Author: Sven Wanner (s.wanner@dkfz.de) - -import unittest - -from hyppopy.helpers import * - - -class HelpersTestSuite(unittest.TestCase): - - def setUp(self): - pass - - -if __name__ == '__main__': - unittest.main() diff --git a/hyppopy/tests/test_solver_factory.py b/hyppopy/tests/test_solver_factory.py index c0d7d7c..a251f74 100644 --- a/hyppopy/tests/test_solver_factory.py +++ b/hyppopy/tests/test_solver_factory.py @@ -1,35 +1,71 @@ # -*- coding: utf-8 -*- # # DKFZ # # # Copyright (c) German Cancer Research Center, # Division of Medical and Biological Informatics. # All rights reserved. # # This software is distributed WITHOUT ANY WARRANTY; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. # # See LICENSE.txt or http://www.mitk.org for details. # # Author: Sven Wanner (s.wanner@dkfz.de) +import os import unittest -from hyppopy.solver_factory import SolverFactory +from sklearn.svm import SVC +from sklearn import datasets +from sklearn.model_selection import cross_val_score +from sklearn.model_selection import train_test_split + +from hyppopy.solverfactory import SolverFactory class SolverFactoryTestSuite(unittest.TestCase): def setUp(self): pass - def test_plugin_load(self): - factory = SolverFactory.instance() - for solver_name in factory.get_solver_names(): - self.assertTrue(factory.get_solver(solver_name).is_ready()) + def test_solver_loading(self): + pass + + def test_iris_solver_execution(self): + iris = datasets.load_iris() + X, X_test, y, y_test = train_test_split(iris.data, iris.target, test_size=0.1, random_state=42) + my_IRIS_dta = [X, y] + + my_SVC_parameter = { + 'C': {'domain': 'uniform', 'data': [0, 20]}, + 'gamma': {'domain': 'uniform', 'data': [0.0001, 20.0]}, + 'kernel': {'domain': 'categorical', 'data': ['linear', 'sigmoid', 'poly', 'rbf']} + } + + def my_SVC_loss_func(data, params): + clf = SVC(**params) + return -cross_val_score(clf, data[0], data[1], cv=3).mean() + + factory = SolverFactory() + + solver = factory.get_solver('optunity') + solver.set_data(my_IRIS_dta) + solver.set_parameters(my_SVC_parameter) + solver.set_loss_function(my_SVC_loss_func) + solver.run() + solver.get_results() + + solver = factory.get_solver('hyperopt') + solver.set_data(my_IRIS_dta) + solver.set_parameters(my_SVC_parameter) + solver.set_loss_function(my_SVC_loss_func) + solver.run() + solver.get_results() if __name__ == '__main__': unittest.main() + diff --git a/hyppopy/workflows/__init__.py b/hyppopy/workflows/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/planning/Hyppopy.vpp b/planning/Hyppopy.vpp new file mode 100644 index 0000000..e3ea8dd Binary files /dev/null and b/planning/Hyppopy.vpp differ diff --git a/setup.py b/setup.py index 7666019..40ba7c1 100644 --- a/setup.py +++ b/setup.py @@ -1,37 +1,37 @@ # -*- coding: utf-8 -*- from setuptools import setup, find_packages with open('README.rst') as f: readme = f.read() with open('LICENSE') as f: license = f.read() setup( name='hyppopy', - version='0.0.0', + version='0.0.1', description='Hyper-Parameter Optimization Toolbox for Blackboxfunction Optimization', long_description=readme, # if you want, put your own name here # (this would likely result in people sending you emails) - author='SvenWanner@DKFZ', + author='Sven Wanner', author_email='s.wanner@dkfz.de', url='', license=license, - packages=find_packages(exclude=('bin', '*test*', 'doc', 'hypopy')), + packages=find_packages(exclude=('bin', '*test*', 'doc', 'hyppopy')), # the requirements to install this project. # Since this one is so simple this is empty. install_requires=[], # a more sophisticated project might have something like: #install_requires=['numpy>=1.11.0', 'scipy>=0.17', 'scikit-learn'] # after running setup.py, you will be able to call hypopy_exe # from the console as if it was a normal binary. It will call the function # main in bin/hypopy_exe.py entry_points={ - 'console_scripts': ['hypopy_exe=bin.hypopy_exe:main'], + 'console_scripts': ['hyppopy_exe=bin.hypopy_exe:main'], } )