diff --git a/hyppopy/globals.py b/hyppopy/globals.py index 819d8f4..b3a656e 100644 --- a/hyppopy/globals.py +++ b/hyppopy/globals.py @@ -1,28 +1,28 @@ # 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__), "..") +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) sys.path.insert(0, ROOT) PLUGIN_DEFAULT_DIR = os.path.join(ROOT, *("hyppopy", "plugins")) LOGFILENAME = os.path.join(ROOT, 'logfile.log') DEBUGLEVEL = logging.DEBUG logging.basicConfig(filename=LOGFILENAME, filemode='w', format='%(levelname)s: %(name)s - %(message)s') diff --git a/hyppopy/plugins/hyperopt_settings_plugin.py b/hyppopy/plugins/hyperopt_settings_plugin.py new file mode 100644 index 0000000..7ca6aff --- /dev/null +++ b/hyppopy/plugins/hyperopt_settings_plugin.py @@ -0,0 +1,64 @@ +# -*- 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 logging +from hyppopy.globals import DEBUGLEVEL +LOG = logging.getLogger(os.path.basename(__file__)) +LOG.setLevel(DEBUGLEVEL) + +from pprint import pformat + +try: + from hyperopt import hp + 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.settingspluginbase import SettingsPluginBase + + +class hyperopt_Settings(SettingsPluginBase, IPlugin): + + def __init__(self): + SettingsPluginBase.__init__(self) + LOG.debug("initialized") + + def convert_parameter(self, input_dict): + LOG.debug(f"convert input parameter\n\n\t{pformat(input_dict)}\n") + + solution_space = {} + for name, content in input_dict.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': + solution_space[name] = domain_fn(name, data) + else: + solution_space[name] = domain_fn(name, data[0], data[1]) + return solution_space + diff --git a/hyppopy/plugins/hyperopt_settings_plugin.yapsy-plugin b/hyppopy/plugins/hyperopt_settings_plugin.yapsy-plugin new file mode 100644 index 0000000..7f41d11 --- /dev/null +++ b/hyppopy/plugins/hyperopt_settings_plugin.yapsy-plugin @@ -0,0 +1,9 @@ +[Core] +Name = hyperopt +Module = hyperopt_settings_plugin + +[Documentation] +Author = Sven Wanner +Version = 0.1 +Website = https://github.com/hyperopt/hyperopt +Description = Hyperopt Settings Plugin \ No newline at end of file diff --git a/hyppopy/plugins/hyperopt_solver_plugin.py b/hyppopy/plugins/hyperopt_solver_plugin.py index f08c273..b99abc5 100644 --- a/hyppopy/plugins/hyperopt_solver_plugin.py +++ b/hyppopy/plugins/hyperopt_solver_plugin.py @@ -1,90 +1,68 @@ # -*- 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 logging from hyppopy.globals import DEBUGLEVEL LOG = logging.getLogger(os.path.basename(__file__)) LOG.setLevel(DEBUGLEVEL) 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): SolverPluginBase.__init__(self) LOG.debug("initialized") 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 input parameter\n\n\t{pformat(params)}\n") - - 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:\n\n\t{pformat(self.solution_space)}\n") + def execute_solver(self, parameter): + LOG.debug(f"execute_solver using solution space:\n\n\t{pformat(parameter)}\n") self.trials = Trials() try: self.best = fmin(fn=self.loss_function, - space=self.solution_space, + space=parameter, 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/plugins/optunity_solver_plugin.py b/hyppopy/plugins/optunity_settings_plugin.py similarity index 52% copy from hyppopy/plugins/optunity_solver_plugin.py copy to hyppopy/plugins/optunity_settings_plugin.py index fb4aa60..5c5a939 100644 --- a/hyppopy/plugins/optunity_solver_plugin.py +++ b/hyppopy/plugins/optunity_settings_plugin.py @@ -1,112 +1,80 @@ # -*- 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 logging from hyppopy.globals import DEBUGLEVEL LOG = logging.getLogger(os.path.basename(__file__)) LOG.setLevel(DEBUGLEVEL) 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 +from hyppopy.settingspluginbase import SettingsPluginBase -class optunity_Solver(SolverPluginBase, IPlugin): - solver_info = None - trials = None - best = None - status = None +class optunity_Settings(SettingsPluginBase, IPlugin): def __init__(self): - SolverPluginBase.__init__(self) + SettingsPluginBase.__init__(self) LOG.debug("initialized") - 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 input parameter\n\n\t{pformat(params)}\n") + def convert_parameter(self, input_dict): + LOG.debug(f"convert input parameter\n\n\t{pformat(input_dict)}\n") # 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 = {} + solution_space = {} # split input in categorical and non-categorical data - cat, uni = split_categorical(params) + cat, uni = split_categorical(input_dict) # 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:\n\n\t{pformat(self.solution_space)}\n") - 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") + solution_space = tmp2 + return solution_space diff --git a/hyppopy/plugins/optunity_settings_plugin.yapsy-plugin b/hyppopy/plugins/optunity_settings_plugin.yapsy-plugin new file mode 100644 index 0000000..b2395aa --- /dev/null +++ b/hyppopy/plugins/optunity_settings_plugin.yapsy-plugin @@ -0,0 +1,9 @@ +[Core] +Name = optunity +Module = optunity_settings_plugin + +[Documentation] +Author = Sven Wanner +Version = 0.1 +Website = https://optunity.readthedocs.io/en/latest/ +Description = Optunity Settings Plugin \ No newline at end of file diff --git a/hyppopy/plugins/optunity_solver_plugin.py b/hyppopy/plugins/optunity_solver_plugin.py index fb4aa60..10322dc 100644 --- a/hyppopy/plugins/optunity_solver_plugin.py +++ b/hyppopy/plugins/optunity_solver_plugin.py @@ -1,112 +1,73 @@ # -*- 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 logging from hyppopy.globals import DEBUGLEVEL LOG = logging.getLogger(os.path.basename(__file__)) LOG.setLevel(DEBUGLEVEL) 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): SolverPluginBase.__init__(self) LOG.debug("initialized") 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 input parameter\n\n\t{pformat(params)}\n") - - # 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:\n\n\t{pformat(self.solution_space)}\n") + def execute_solver(self, parameter): + LOG.debug(f"execute_solver using solution space:\n\n\t{pformat(parameter)}\n") 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) + search_space=parameter) 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/solverpluginbase.py b/hyppopy/settingspluginbase.py similarity index 50% copy from hyppopy/solverpluginbase.py copy to hyppopy/settingspluginbase.py index 7a8c407..0b3ee28 100644 --- a/hyppopy/solverpluginbase.py +++ b/hyppopy/settingspluginbase.py @@ -1,74 +1,70 @@ # -*- 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 os import logging -LOG = logging.getLogger('hyppopy') +from hyppopy.globals import DEBUGLEVEL +LOG = logging.getLogger(os.path.basename(__file__)) +LOG.setLevel(DEBUGLEVEL) +from hyppopy.deepdict.deepdict import DeepDict -class SolverPluginBase(object): - data = None - loss = None - solution_space = None + +class SettingsPluginBase(object): + _data = 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') + self._data = DeepDict() @abc.abstractmethod - def convert_parameter(self, params): + def convert_parameter(self): 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 get_hyperparameter(self): + return self.convert_parameter(self.data.data["hyperparameter"]) - def set_data(self, data): - self.data = data + def set(self, data): + self.data.clear() + self.data.data = data - def set_parameters(self, params): - self.convert_parameter(params=params) + def read(self, fname): + self.data.from_file(fname) - def set_loss_function(self, func): - self.loss = func + def write(self, fname): + self.data.to_file(fname) - def get_results(self): - self.convert_results() + @property + def data(self): + return self._data - def run(self): - self.execute_solver() + @data.setter + def data(self, value): + return self._data @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/solver.py b/hyppopy/solver.py index 2edac91..476693d 100644 --- a/hyppopy/solver.py +++ b/hyppopy/solver.py @@ -1,75 +1,81 @@ # -*- 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 logging -LOG = logging.getLogger('hyppopy') +from hyppopy.globals import DEBUGLEVEL +LOG = logging.getLogger(os.path.basename(__file__)) +LOG.setLevel(DEBUGLEVEL) class Solver(object): _name = None _solver_plugin = None _settings_plugin = 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) + self.settings.set(params) + + def read_parameter(self, fname): + self.settings.read(fname) def set_loss_function(self, loss_func): self.solver.set_loss_function(loss_func) def run(self): - self.solver.run() + self.solver.run(self.settings.get_hyperparameter()) def get_results(self): self.solver.get_results() @property def is_ready(self): return self.solver is not None and self.settings is not None @property def solver(self): return self._solver_plugin @solver.setter def solver(self, 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 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/solverfactory.py b/hyppopy/solverfactory.py index 7e4e188..1de0713 100644 --- a/hyppopy/solverfactory.py +++ b/hyppopy/solverfactory.py @@ -1,149 +1,153 @@ # -*- 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.globals import PLUGIN_DEFAULT_DIR from hyppopy.solver import Solver import os import logging from hyppopy.globals import DEBUGLEVEL LOG = logging.getLogger(os.path.basename(__file__)) LOG.setLevel(DEBUGLEVEL) class SolverFactory(object): """ This class is responsible for grabbing all plugins from the plugin folder arranging them into a Solver class instances. These Solver class instances can be requested from the factory via the get_solver method. The SolverFactory class is a Singleton class, so try not to instantiate it using SolverFactory(), the consequences will be horrific. Instead use factory = SolverFactory.instance(). """ _instance = None _locked = True _plugin_dirs = [] _plugins = {} def __init__(self): if self._locked: msg = "!!! seems you used SolverFactory() to get an instance, please don't do that, "\ "it will kill a cute puppy anywhere close to you! SolverFactory is a "\ "Singleton, means please use SolverFactory.instance() instead !!!" LOG.error(msg) raise AssertionError(msg) if SolverFactory._instance is not None: pass else: SolverFactory._instance = self self.reset() self.load_plugins() LOG.debug("initialized") @staticmethod def instance(): """ Singleton instance access :return: [SolverFactory] instance """ SolverFactory._locked = False LOG.debug("instance request") if SolverFactory._instance is None: SolverFactory() SolverFactory._locked = True return SolverFactory._instance def load_plugins(self): """ Load plugin modules from plugin paths """ LOG.debug("load_plugins()") manager = PluginManager() LOG.debug(f"setPluginPlaces(" + " ".join(map(str, self._plugin_dirs))) manager.setPluginPlaces(self._plugin_dirs) manager.collectPlugins() for plugin in manager.getAllPlugins(): name_elements = plugin.plugin_object.__class__.__name__.split("_") LOG.debug("found plugin " + " ".join(map(str, name_elements))) 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__() + obj = plugin.plugin_object.__class__() + obj.name = name_elements[0] + self._plugins[name_elements[0]].solver = obj 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": + elif name_elements[1] == "Settings": try: - self._plugins[name_elements[0]].settings = plugin.plugin_object.__class__() + obj = plugin.plugin_object.__class__() + obj.name = name_elements[0] + self._plugins[name_elements[0]].settings = obj 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!") if len(self._plugins) == 0: msg = "no plugins found, please check your plugin folder names or your plugin scripts for errors!" LOG.error(msg) raise IOError(msg) 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 list_solver(self): """ list all solvers available :return: [list(str)] """ return list(self._plugins.keys()) def get_solver(self, name): """ returns a solver by name tag :param name: [str] solver name :return: [Solver] instance """ if not isinstance(name, str): msg = f"Invalid input, str type expected for name, got {type(name)} instead" LOG.error(msg) raise IOError(msg) if name not in self.list_solver(): msg = f"failed solver request, a solver called {name} is not available, " \ f"check for typo or if your plugin failed while loading!" LOG.error(msg) raise LookupError(msg) LOG.debug(f"get_solver({name})") return self._plugins[name] diff --git a/hyppopy/solverpluginbase.py b/hyppopy/solverpluginbase.py index 7a8c407..aa14611 100644 --- a/hyppopy/solverpluginbase.py +++ b/hyppopy/solverpluginbase.py @@ -1,74 +1,69 @@ # -*- 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 os import logging -LOG = logging.getLogger('hyppopy') +from hyppopy.globals import DEBUGLEVEL +LOG = logging.getLogger(os.path.basename(__file__)) +LOG.setLevel(DEBUGLEVEL) 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() + def run(self, parameter): + self.execute_solver(parameter) @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/data/iris_svc_parameter.json b/hyppopy/tests/data/iris_svc_parameter.json new file mode 100644 index 0000000..eb37b87 --- /dev/null +++ b/hyppopy/tests/data/iris_svc_parameter.json @@ -0,0 +1,17 @@ +{"hyperparameter": { + "C": { + "domain": "uniform", + "data": "[0,20]", + "type": "float" + }, + "gamma": { + "domain": "uniform", + "data": "[0.0001,20.0]", + "type": "float" + }, + "kernel": { + "domain": "categorical", + "data": "[linear, sigmoid, poly, rbf]", + "type": "str" + } +}} \ No newline at end of file diff --git a/hyppopy/tests/data/iris_svc_paramset.json b/hyppopy/tests/data/iris_svc_paramset.json deleted file mode 100644 index 7bdad4f..0000000 --- a/hyppopy/tests/data/iris_svc_paramset.json +++ /dev/null @@ -1,14 +0,0 @@ -{"hyperparameter": { - "C": { - "domain": "uniform", - "data": "[0, 20]", - "type": "float"}, - "kernel": { - "domain": "choice", - "data": "[linear, sigmoid, poly, rbf]", - "type": "str"}, - "gamma": { - "domain": "uniform", - "data": "[0.0001, 20]", - "type": "float"} -}} \ No newline at end of file diff --git a/hyppopy/tests/test_solver_factory.py b/hyppopy/tests/test_solver_factory.py index 63e5833..5527935 100644 --- a/hyppopy/tests/test_solver_factory.py +++ b/hyppopy/tests/test_solver_factory.py @@ -1,75 +1,78 @@ # -*- 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 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 +from hyppopy.globals import ROOT +TESTPARAMFILE = os.path.join(ROOT, *('hyppopy', 'tests', 'data', 'iris_svc_parameter.json')) + class SolverFactoryTestSuite(unittest.TestCase): def setUp(self): pass def test_solver_loading(self): factory = SolverFactory.instance() names = factory.list_solver() self.assertTrue("hyperopt" in names) self.assertTrue("optunity" in names) 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']} - } + my_SVC_parameter = {'hyperparameter': { + 'C': {'domain': 'uniform', 'data': [0, 20], 'type': 'float'}, + 'gamma': {'domain': 'uniform', 'data': [0.0001, 20.0], 'type': 'float'}, + 'kernel': {'domain': 'categorical', 'data': ['linear', 'sigmoid', 'poly', 'rbf'], 'type': 'str'} + }} def my_SVC_loss_func(data, params): clf = SVC(**params) return -cross_val_score(clf, data[0], data[1], cv=3).mean() factory = SolverFactory.instance() names = factory.list_solver() solver = factory.get_solver('optunity') solver.set_data(my_IRIS_dta) - solver.set_parameters(my_SVC_parameter) + solver.read_parameter(TESTPARAMFILE) 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/resources/logo.svg b/resources/logo.svg new file mode 100644 index 0000000..88eda62 --- /dev/null +++ b/resources/logo.svg @@ -0,0 +1,274 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/logo_512x512.png b/resources/logo_512x512.png new file mode 100644 index 0000000..bf610c5 Binary files /dev/null and b/resources/logo_512x512.png differ diff --git a/resources/logo_64x64.png b/resources/logo_64x64.png new file mode 100644 index 0000000..45cb9df Binary files /dev/null and b/resources/logo_64x64.png differ