Project
Loading...
Searching...
No Matches
test_HuffmanCodec.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
16
17#define BOOST_TEST_MODULE HuffmanCodec unit test
18#define BOOST_TEST_MAIN
19#define BOOST_TEST_DYN_LINK
20#include <boost/test/unit_test.hpp>
21#include <filesystem>
22#include <iostream>
23#include <iomanip>
24#include <sstream>
25#include <vector>
26#include <bitset>
27#include <thread>
28#include <stdexcept> // exeptions, runtime_error
29#include "../include/DataCompression/dc_primitives.h"
30#include "../include/DataCompression/HuffmanCodec.h"
32#include "DataGenerator.h"
33#include "Fifo.h"
34
35namespace o2
36{
37namespace data_compression
38{
39
40// a decoder process working on the FIFO of encoded data and comparing to
41// original data from the corresponding FIFO
42template <class RandvalStreamT, class EncodedStreamT, class CodecT>
43void decoderProcess(RandvalStreamT& fifoRandvals, EncodedStreamT& fifoEncoded, CodecT& codec)
44{
45 uint16_t decodedLen = 0;
46 typename CodecT::model_type::value_type decodedValue;
47 do {
48 } while (fifoEncoded.pull([&](typename EncodedStreamT::value_type c) {
49 codec.Decode(decodedValue, c, decodedLen);
50 return fifoRandvals.pull([&](typename RandvalStreamT::value_type v) {
51 if (decodedValue != v) {
52 throw std::runtime_error("decoding mismatch");
53 return false;
54 } else {
55 // std::cout << "decoded: "
56 // << std::setw(4) << decodedValue
57 // << " code: " << c
58 // << std::endl;
59 }
60 return true;
61 });
62 }));
63}
64
65template <typename CodecT, typename GeneratorT>
66void checkRandom(CodecT& codec, GeneratorT& generator, int nRolls = 1000000)
67{
68 using ValueT = typename CodecT::value_type;
69 using CodeT = typename CodecT::code_type;
70 auto const& huffmanmodel = codec.getCodingModel();
71
73 // test loop for random values
74 //
75
76 // FIFO for the random numbers
77 o2::test::Fifo<ValueT> fifoRandvals;
78
79 // FIFO for encoded values
80 using FifoBuffer_t = o2::test::Fifo<uint32_t>;
81 FifoBuffer_t fifoEncoded;
82
83 int n = nRolls;
84 std::cout << std::endl
85 << "Testing encoding-decoding with " << nRolls << " random value(s) ..." << std::endl;
86
87 std::thread decoderThread([&]() { decoderProcess(fifoRandvals, fifoEncoded, codec); });
88
89 while (n-- > 0) {
90 uint16_t codeLen = 0;
91 CodeT code;
92 ValueT value = generator();
93 codec.Encode(value, code, codeLen);
94 fifoRandvals.push(value);
95 if (huffmanmodel.OrderMSB) {
96 code <<= (code.size() - codeLen);
97 }
98 fifoEncoded.push(code.to_ulong(), n == 0);
99 // std::cout << "encoded: " << std::setw(4) << value << " code: " << code << std::endl;
100 }
101
102 decoderThread.join();
103
104 std::cout << "... done" << std::endl;
105}
106
108{
109 // defining a contiguous alphabet of integral 16 bit unsigned numbers
110 // in the range [-7, 10] including the upper bound
111 // the first definition is a data type, then an object of this type is
112 // defined
113 using TestDistribution_t = o2::test::normal_distribution<double>;
115 DataGenerator_t dg(-7, 10, 1, 0., 1.);
116 using SimpleRangeAlphabet_t = ContiguousAlphabet<DataGenerator_t::value_type, -7, 10>;
117 SimpleRangeAlphabet_t alphabet;
118
120 // Using the Huffman propability model for the alphabet
121 //
122 // HuffmanModel_t is a data type, huffmanmodel an object of this type
123 // the node type is defined to be HuffmanNode specialized to bitset<16>
124 // third template parameter determines whether code has to be decoded
125 // MSB to LSB (true) or LSB to MSB (false)
126 using HuffmanModel_t =
128 HuffmanModel_t huffmanmodel;
129
130 huffmanmodel.init(0.);
131 if (verbosity > 0) {
132 std::cout << std::endl
133 << "Huffman probability model after initialization: " << std::endl;
134 for (auto s : alphabet) {
135 std::cout << "val = " << std::setw(2) << s << " --> weight = " << huffmanmodel[s] << std::endl;
136 }
137 }
138
139 // add probabilities from data generator as weights for every symbol
140 for (auto s : alphabet) {
141 huffmanmodel.addWeight(s, dg.getProbability(s));
142 }
143
144 // normalizing the weight to the total weight thus having the probability
145 // for every symbol
146 huffmanmodel.normalize();
147 if (verbosity > 0) {
148 std::cout << std::endl
149 << "Probabilities from DataGenerator:" << std::endl;
150 for (auto i : huffmanmodel) {
151 std::cout << "val = " << std::setw(2) << i.first << " --> weight = " << i.second << std::endl;
152 }
153 }
154
156 // generate the Huffman tree in the Huffman model and create a Huffman
157 // codec operating on the probability table
158 huffmanmodel.GenerateHuffmanTree();
159 if (verbosity > 0) {
160 std::cout << std::endl
161 << "Generating binary tree and Huffman codes" << std::endl;
162 huffmanmodel.print();
163 }
164 return std::pair<HuffmanCodec<HuffmanModel_t>, DataGenerator_t>(huffmanmodel, dg);
165}
166
167BOOST_AUTO_TEST_CASE(test_HuffmanCodec_basic)
168{
169 auto setup = setupCodec();
170 auto& codec = setup.first;
171 auto& dg = setup.second;
172 auto const& huffmanmodel = codec.getCodingModel();
173 using ValueT = decltype(setup.first)::value_type;
174 using CodeT = decltype(setup.first)::code_type;
175
177 // print Huffman code summary and perform an encoding-decoding check for
178 // every symbol
179 std::cout << std::endl
180 << "Huffman code summary: " << (huffmanmodel.OrderMSB ? "MSB to LSB" : "LSB to MSB") << std::endl;
181 for (auto const& i : huffmanmodel) {
182 uint16_t codeLen = 0;
183 CodeT code;
184 codec.Encode(i.first, code, codeLen);
185 std::cout << "value: " << std::setw(4) << i.first << " code length: " << std::setw(3) << codeLen << " code: ";
186 if (not huffmanmodel.OrderMSB) {
187 std::cout << std::setw(code.size() - codeLen);
188 }
189 for (int k = 0; k < codeLen; k++) {
190 std::cout << code[codeLen - 1 - k];
191 }
192 std::cout << std::endl;
193 if (huffmanmodel.OrderMSB) {
194 code <<= (code.size() - codeLen);
195 }
196 uint16_t decodedLen = 0;
197 ValueT value;
198 codec.Decode(value, code, decodedLen);
199 if (codeLen != decodedLen || value != i.first) {
200 std::cout << "mismatch in decoded value: " << value << "(" << decodedLen << ")" << std::endl;
201 }
202 }
203
204 checkRandom(codec, dg);
205}
206
207BOOST_AUTO_TEST_CASE(test_HuffmanCodec_configuration)
208{
209 auto setup = setupCodec();
210 auto& codec = setup.first;
211 auto& dg = setup.second;
212 auto const& huffmanmodel = codec.getCodingModel();
213 using ValueT = decltype(setup.first)::value_type;
214 using CodeT = decltype(setup.first)::code_type;
215
216 // check writing and reading of the huffman configuration
217 std::stringstream filename;
218 filename << o2::utils::Str::create_unique_path(std::filesystem::temp_directory_path().native()) << "_testHuffmanCodec.zlib";
219
220 auto nNodes = codec.writeConfiguration(filename.str().c_str(), "zlib");
221 BOOST_CHECK(nNodes > 0);
222 auto result = codec.loadConfiguration(filename.str().c_str(), "zlib");
223 BOOST_CHECK(result == 0);
224 std::filesystem::remove(filename.str());
225
226 checkRandom(codec, dg);
227}
228
229} // namespace data_compression
230} // namespace o2
#define verbosity
A simple data generator.
unsigned short int uint16_t
Thread safe FIFO.
int32_t i
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error s
uint32_t c
Definition RawData.h:2
A simple data generator.
A thread safe FIFO.
Definition Fifo.h:60
void push(T something, bool isLast=false)
Definition Fifo.h:68
specialization of std::normal_distribution which implements also the analytic formula.
GLdouble n
Definition glcorearb.h:1982
GLuint64EXT * result
Definition glcorearb.h:5662
GLsizei const GLfloat * value
Definition glcorearb.h:819
void decoderProcess(RandvalStreamT &fifoRandvals, EncodedStreamT &fifoEncoded, CodecT &codec)
void checkRandom(CodecT &codec, GeneratorT &generator, int nRolls=1000000)
auto setupCodec(int verbosity=0)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
BOOST_AUTO_TEST_CASE(FlatHisto)
std::string filename()
static std::string create_unique_path(const std::string_view prefix="", int length=16)
size_t nRolls
BOOST_CHECK(tree)