File: monitor.py

package info (click to toggle)
python-bumps 1.0.0b2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,144 kB
  • sloc: python: 23,941; xml: 493; ansic: 373; makefile: 209; sh: 91; javascript: 90
file content (137 lines) | stat: -rw-r--r-- 4,173 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# This program is in the public domain
"""
Progress monitors.

Process monitors accept a :class:`bumps.history.History` object each cycle
and perform some sort of work.

Monitors have a :meth:`Monitor.config_history` method which calls
*history.requires()* to set the amount of history it needs and a
*Monitor.__call__* method which takes the updated history and
generates the monitor output.

Most monitors are subclassed from :class:`TimedUpdate` to set a minimum
time between updates and to only show updates when there is an improvement.
The *TimedUpdate* subclasses must override :meth:`TimedUpdate.show_progress`
and :meth:`TimedUpdate.show_improvement` to control the output form. History
must be updated with time, value, point and step. The
bumps.fitters.MonitorRunner class manages history and updates.
"""

__all__ = ["Monitor", "Logger", "TimedUpdate"]

from numpy import inf


class Monitor(object):
    """
    Base class for monitors.
    """

    def config_history(self, history):
        """
        Indicate which fields are needed by the monitor and for what duration.
        """
        pass

    def __call__(self, history):
        """
        Give the monitor a new piece of history to work with.
        """
        pass


def _getfield(history, field):
    """
    Return the last value in the trace, or None if there is no
    last value or no trace.
    """
    trace = getattr(history, field, [])
    try:
        return trace[0]
    except IndexError:
        return None


class Logger(Monitor):
    """
    Keeps a record of all values for the desired fields.

    *fields* is a list of history fields to store.

    *table* is an object with a *store(field=value,...)* method, which gets
    the current value of each field every time the history is updated.

    Call :meth:`config_history` with the :class:`bumps.history.History`
    object before starting so that the correct fields are stored.
    """

    def __init__(self, fields=(), table=None):
        self.fields = fields
        self.table = table

    def config_history(self, history):
        """
        Make sure history records each logged field.
        """
        kwargs = dict((key, 1) for key in self.fields)
        history.requires(**kwargs)

    def __call__(self, history):
        """
        Record the next piece of history.
        """
        record = dict((f, _getfield(history, f)) for f in self.fields)
        self.table.store(**record)


class TimedUpdate(Monitor):
    """
    Indicate progress every n seconds.

    The process should provide time, value, point, and step to the
    history update. Call :meth:`config_history` with the
    :class:`bumps.history.History` object before starting so that
    these fields are stored.

    *progress* is the number of seconds to go before showing progress, such
    as time or step number.

    *improvement* is the number of seconds to go before showing
    improvements to value.

    By default, the update only prints step number and improved value.
    Subclass TimedUpdate with replaced :meth:`show_progress` and
    :meth:`show_improvement` to trigger GUI updates or show parameter
    values.
    """

    def __init__(self, progress=60, improvement=5):
        self.progress_delta = progress
        self.improvement_delta = improvement
        self.progress_time = -inf
        self.improvement_time = -inf
        self.value = inf
        self.improved = False

    def config_history(self, history):
        history.requires(time=1, value=1, point=1, step=1)

    def show_improvement(self, history):
        print("step", history.step, "value", history.value)

    def show_progress(self, history):
        pass

    def __call__(self, history):
        t = history.time[0]
        v = history.value[0]
        if v < self.value:
            self.improved = True
        if t > self.progress_time + self.progress_delta:
            self.progress_time = t
            self.show_progress(history)
        if self.improved and t > self.improvement_time + self.improvement_delta:
            self.improved = False
            self.improvement_time = t
            self.show_improvement(history)