status.hh 8.63 KB
Newer Older
1
2
3
4
5
6
7
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*****************************************************************************
 *   See the file COPYING for full copying permissions.                      *
 *                                                                           *
 *   This program is free software: you can redistribute it and/or modify    *
 *   it under the terms of the GNU General Public License as published by    *
Dennis Gläser's avatar
Dennis Gläser committed
8
 *   the Free Software Foundation, either version 3 of the License, or       *
9
10
11
12
13
14
15
16
17
18
19
20
 *   (at your option) any later version.                                     *
 *                                                                           *
 *   This program is distributed in the hope that it will be useful,         *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
 *   GNU General Public License for more details.                            *
 *                                                                           *
 *   You should have received a copy of the GNU General Public License       *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
 *****************************************************************************/
/*!
 * \file
21
 * \ingroup Sampling
22
23
24
25
26
27
 * \brief Class that can be used to define target counters in
 *        sampling procedures and which outputs the current status.
 */
#ifndef FRACKIT_SAMPLING_STATUS_HH
#define FRACKIT_SAMPLING_STATUS_HH

28
#include <string>
29
30
#include <iomanip>
#include <iostream>
31
#include <algorithm>
32
#include <numeric>
33
#include <utility>
34
35
36
37
38
39
40
41
#include <unordered_map>
#include <initializer_list>

#include <frackit/common/id.hh>

namespace Frackit {

/*!
42
 * \ingroup Sampling
43
44
45
46
47
48
49
50
51
 * \brief Class that can be used to define target counters in
 *        sampling procedures and which outputs the current status.
 */
class SamplingStatus
{

public:

    /*!
52
     * \brief Set the target sample count for a specific id.
53
54
55
56
57
58
59
60
     */
    void setTargetCount(const Id& id, std::size_t targetCount)
    {
        targetCount_[id.get()] = targetCount;

        auto it = count_.find(id.get());
        if (it == count_.end())
            count_[id.get()] = 0;
61
62
63
64
        else if (it->second > targetCount)
            std::cout << "Warning: given target count is below current count" << std::endl;
    }

Dennis Gläser's avatar
Dennis Gläser committed
65
66
67
68
69
70
    /*!
     * \brief Reset everything.
     */
    void reset()
    {
        headerPrinted_ = false;
71
72
        unspecifiedRejectedCount_ = 0;

Dennis Gläser's avatar
Dennis Gläser committed
73
74
        count_.clear();
        targetCount_.clear();
75
        rejectedCount_.clear();
Dennis Gläser's avatar
Dennis Gläser committed
76
77
    }

78
79
80
81
82
    /*!
     * \brief Reset all counters.
     */
    void resetCounters()
    {
83
84
85
        std::for_each(count_.begin(),
                      count_.end(),
                      [] (auto& idCountPair) { idCountPair.second = 0.0; });
86
87
88
89
90
91
92
93
    }

    /*!
     * \brief Reset counter for the given id.
     */
    void resetCounter(const Id& id)
    {
        count_[id.get()] = 0;
94
95
96
    }

    /*!
97
98
     * \brief Returns true when the target
     *        sample count is reached.
99
100
101
102
     */
    bool finished()
    {
        for (const auto& pair : count_)
103
            if (pair.second < targetCount_.at(pair.first))
104
105
106
107
108
                return false;
        return true;
    }

    /*!
109
110
     * \brief Returns true when the target count
     *        for a specific id is reached.
111
112
113
114
115
116
     */
    bool finished(const Id& id)
    {
        auto it = count_.find(id.get());
        if (it == count_.end())
            throw std::runtime_error("Target count not set for given id");
117
        return it->second >= targetCount_.at(it->first);
118
119
120
    }

    /*!
121
     * \brief Increase counter for a specific id.
122
123
124
125
     */
    void increaseCounter(const Id& id)
    {
        count_[id.get()]++;
126
127
        if (count_[id.get()] > targetCount_[id.get()])
            std::cout << "Warning: target count for id " << id.get() << " was surpassed" << std::endl;
128
129
130
    }

    /*!
131
     * \brief Increase counter of rejected samples.
132
133
134
     */
    void increaseRejectedCounter()
    {
135
136
137
138
139
140
141
142
143
144
        unspecifiedRejectedCount_++;
    }

    /*!
     * \brief Increase counter of rejected samples
     *        with a specific rejection reason.
     */
    void increaseRejectedCounter(const std::string& reason)
    {
        rejectedCount_[reason]++;
145
146
    }

147
148
149
150
151
152
153
154
155
    /*!
     * \brief Returns the entity count for the given id.
     */
    std::size_t getCount(const Id& id)
    { return count_[id.get()]; }

    /*!
     * \brief Returns the overall entity count.
     */
156
    std::size_t getCount() const
157
158
159
160
161
162
163
164
    {
        return std::accumulate(count_.begin(),
                               count_.end(),
                               0,
                               [] (const auto& curCount, const auto& idCountPair)
                               { return curCount + idCountPair.second; });
    }

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
    /*!
     * \brief Get information on rejection events.
     */
    auto getRejectionData() const
    {
        auto result = rejectedCount_;
        if (unspecifiedRejectedCount_ > 0)
        {
            if (result.find("unspecified") != result.end())
            {
                std::cout << "WARNING:\n"
                          << "Rejection reason \"unspecified\" registered, "
                          << "as well as rejection events without reason.\n"
                          << "These two counters will be added.\n";
                result["unlabeled"] += unspecifiedRejectedCount_;
            }
            else
                result["unlabeled"] = unspecifiedRejectedCount_;
        }

        return result;
    }

188
    /*!
189
     * \brief Print current status to terminal.
190
191
192
193
194
     */
    void print(bool forceHeaderPrint = false)
    {
        if (!headerPrinted_ || forceHeaderPrint)
        {
195
196
197
            std::cout << "#####################################################################################\n"
                      << "# Accepted count   |   Rejected count   |   Acceptance Ratio [%]   |   Progress [%] #\n"
                      << "#-----------------------------------------------------------------------------------#\n";
198
199
200
201
202
            headerPrinted_ = true;
        }

        std::size_t curCount = 0;
        std::size_t curTargetCount = 0;
203
        std::size_t curRejectedCount = unspecifiedRejectedCount_;
204
205
        for (const auto& pair : count_) curCount += pair.second;
        for (const auto& pair : targetCount_) curTargetCount += pair.second;
206
        for (const auto& pair : rejectedCount_) curRejectedCount += pair.second;
207

208
        const auto ratio = 100.0*double(double(curCount)/double(curCount+curRejectedCount));
209
210
        const auto progress = 100.0*double(curCount)/double(curTargetCount);

211
212
213
214
215
        std::cout << std::setprecision(2) << std::fixed;
        const auto ratioNumChars = std::to_string(int(ratio)).size() + 3;
        const auto progressNumChars = std::to_string(int(progress)).size() + 3;

        const auto countString = std::to_string(curCount);
216
        const auto rejectedCountString = std::to_string(curRejectedCount);
217
218
219
220
221
222
223
224
225
226
227
228

        using std::max;
        const std::size_t zero = 0;
        const std::size_t paddingCount    = max(zero, 13 - countString.size());
        const std::size_t paddingRejected = max(zero, 13 - rejectedCountString.size());
        const std::size_t paddingRatio    = max(zero, 16 - ratioNumChars);
        const std::size_t paddingProgess  = max(zero, 8  - progressNumChars);

        std::cout << "  "      << std::string( paddingCount, ' ')    << countString + ' '
                  << "   |   " << std::string( paddingRejected, ' ') << rejectedCountString + ' '
                  << "   |   " << std::string( paddingRatio, ' ')    << ratio << std::string(4, ' ')
                  << "   |   " << std::string( paddingProgess, ' ')  << progress << std::endl;
229
230
    }

231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
    /*!
     * \brief Print information on rejection events.
     */
    void printRejectionData() const
    {
        std::size_t sumRejected = 0;
        const auto data = getRejectionData();
        for (const auto& [reason, count] : data)
            sumRejected += count;
        for (const auto& [reason, count] : data)
            std::cout << "\t-" << reason << ": " << count << " / "
                                                 << std::setprecision(2) << std::fixed
                                                 << 100.0*count/sumRejected << " %" << std::endl;
    }

246
247
private:
    bool headerPrinted_ = false;
248

249
250
    std::unordered_map<std::size_t, std::size_t> count_;
    std::unordered_map<std::size_t, std::size_t> targetCount_;
251
252
    std::unordered_map<std::string, std::size_t> rejectedCount_;
    std::size_t unspecifiedRejectedCount_ = 0;
253
254
255
256
257
};

} // end namespace Frackit

#endif // FRACKIT_SAMPLING_STATUS_HH