diff --git a/.gitignore b/.gitignore index 943351be..aff23183 100644 --- a/.gitignore +++ b/.gitignore @@ -53,4 +53,6 @@ docs/build .spyderworkspace .spyproject .mypy_cache/ -.vscode/ \ No newline at end of file +.vscode/ +*.log +*.ini diff --git a/exopy/tasks/tasks/logic/declarations.enaml b/exopy/tasks/tasks/logic/declarations.enaml index 171107f6..9a2148f8 100644 --- a/exopy/tasks/tasks/logic/declarations.enaml +++ b/exopy/tasks/tasks/logic/declarations.enaml @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- -# Copyright 2015-2018 by Exopy Authors, see AUTHORS for more details. +# Copyright 2015-2023 by Exopy Authors, see AUTHORS for more details. # # Distributed under the terms of the BSD license. # @@ -45,4 +45,8 @@ enamldef LogicTasks(Tasks): Interface: interface = 'loop_linspace_interface:LinspaceLoopInterface' - views = ['views.loop_linspace_view:LinspaceLoopView'] \ No newline at end of file + views = ['views.loop_linspace_view:LinspaceLoopView'] + + Interface: + interface = 'loop_logspace_interface:LogspaceLoopInterface' + views = ['views.loop_logspace_view:LogspaceLoopView'] \ No newline at end of file diff --git a/exopy/tasks/tasks/logic/loop_logspace_interface.py b/exopy/tasks/tasks/logic/loop_logspace_interface.py new file mode 100644 index 00000000..174bea87 --- /dev/null +++ b/exopy/tasks/tasks/logic/loop_logspace_interface.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright 2015-2023 by Exopy Authors, see AUTHORS for more details. +# +# Distributed under the terms of the BSD license. +# +# The full license is in the file LICENCE, distributed with this software. +# ----------------------------------------------------------------------------- +"""Interface allowing to use a logspace in a LoopTask + +""" +import numbers +from decimal import Decimal + +import numpy as np +from atom.api import Str + +from ..task_interface import TaskInterface +from ..validators import Feval + + +class LogspaceLoopInterface(TaskInterface): + """ Common logic for all loop tasks. + + """ + #: Value of exponent at which to start the loop. + start = Str('0.0').tag(pref=True, feval=Feval(types=numbers.Real)) + + #: Value of exponent at which to stop the loop (included) + stop = Str('1.0').tag(pref=True, feval=Feval(types=numbers.Real)) + + #: Step size between exponent values. + step = Str('0.1').tag(pref=True, feval=Feval(types=numbers.Real)) + + #: Base value. + base = Str().tag(pref=True, feval=Feval(types=numbers.Real)) + + def check(self, *args, **kwargs): + """Check evaluation of all loop parameters. + + """ + task = self.task + err_path = task.path + '/' + task.name + test, traceback = super(LogspaceLoopInterface, + self).check(*args, **kwargs) + + if not test: + return test, traceback + + start = task.format_and_eval_string(self.start) + stop = task.format_and_eval_string(self.stop) + step = task.format_and_eval_string(self.step) + if 'value' in task.database_entries: + task.write_in_database('value', start) + + try: + num = int(abs((stop - start)/step)) + 1 + task.write_in_database('point_number', num) + except Exception as e: + test = False + mess = 'Loop task did not succeed to compute the point number: {}' + traceback[err_path + '-points'] = mess.format(e) + return test, traceback + + try: + np.arange(start, stop, step) + except Exception as e: + test = False + mess = 'Loop task did not succeed to create an arange: {}' + traceback[err_path + '-arange'] = mess.format(e) + + return test, traceback + + def perform(self): + """Build the arange and pass it to the LoopTask. + + """ + task = self.task + start = task.format_and_eval_string(self.start) + stop = task.format_and_eval_string(self.stop) + step = task.format_and_eval_string(self.step) + base = task.format_and_eval_string(self.base) + + # Make sure the sign of the step makes sense. + step = -abs(step) if start > stop else abs(step) + + # Compute the number of steps we need. + num = int(round(abs(((stop - start)/step)))) + 1 + + # Update stop to make sure that the generated step is close to the user + # specified one. + stop_digit = abs(Decimal(str(stop)).as_tuple().exponent) + start_digit = abs(Decimal(str(start)).as_tuple().exponent) + step_digit = abs(Decimal(str(step)).as_tuple().exponent) + digit = max((start_digit, step_digit, stop_digit)) + stop = round(start + (num-1)*step, digit) + + # Round values to the maximal number of digit used in start, stop and + # step so that we never get issues with floating point rounding issues. + # The max is used to allow from 1.01 to 2.01 by 0.1 + raw_values = np.logspace(start=start, stop=stop, num=num, base=base) + iterable = np.fromiter((round(value, digit) + for value in raw_values), + np.float64, len(raw_values)) + task.write_in_database('loop_values', np.array(iterable)) + task.perform_loop(iterable) diff --git a/exopy/tasks/tasks/logic/views/loop_logspace_view.enaml b/exopy/tasks/tasks/logic/views/loop_logspace_view.enaml new file mode 100644 index 00000000..19ce5682 --- /dev/null +++ b/exopy/tasks/tasks/logic/views/loop_logspace_view.enaml @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright 2015-2023 by Exopy Authors, see AUTHORS for more details. +# +# Distributed under the terms of the BSD license. +# +# The full license is in the file LICENCE, distributed with this software. +# ----------------------------------------------------------------------------- +"""View for the LinspaceLoopInterface. + +""" +from enaml.widgets.api import (Container, Label, Splitter, SplitItem) + +from .....utils.widgets.qt_completers import QtLineCompleter +from ...string_evaluation import EVALUATER_TOOLTIP + + +enamldef LogspaceLoopView(Splitter): view: + """View for the LogspaceLoopInterface. + + """ + #: Reference to the interface to which this view is linked. + attr interface + + #: Reference to the root view. + attr root + + SplitItem: + Container: + padding = 0 + Label: lab_start: + text = 'Start' + QtLineCompleter: val_start: + text := interface.start + entries_updater << \ + interface.task.list_accessible_database_entries + tool_tip = "Exponent of first value in logspace list" + + SplitItem: + Container: + padding = 0 + Label: lab_stop: + text = 'Stop' + QtLineCompleter: val_stop: + text := interface.stop + entries_updater << \ + interface.task.list_accessible_database_entries + tool_tip = "Exponent of last value in logspace list" + + SplitItem: + Container: + padding = 0 + Label: lab_step: + text = 'Step' + QtLineCompleter: val_step: + text := interface.step + entries_updater << \ + interface.task.list_accessible_database_entries + tool_tip = "Step size of exponent" + SplitItem: + Container: + padding = 0 + Label: lab_base: + text = 'Base' + QtLineCompleter: val_base: + text := interface.base + entries_updater << \ + interface.task.list_accessible_database_entries + tool_tip = "Base value" + diff --git a/exopy/tasks/tasks/logic/views/loop_view.enaml b/exopy/tasks/tasks/logic/views/loop_view.enaml index 136cbe1a..eaeb7f5b 100644 --- a/exopy/tasks/tasks/logic/views/loop_view.enaml +++ b/exopy/tasks/tasks/logic/views/loop_view.enaml @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- -# Copyright 2015-2018 by Exopy Authors, see AUTHORS for more details. +# Copyright 2015-2023 by Exopy Authors, see AUTHORS for more details. # # Distributed under the terms of the BSD license. #