9 #include <svd/reconstruction/NNWaveFitter.h>
14 #include <boost/property_tree/ptree.hpp>
15 #include <boost/property_tree/xml_parser.hpp>
16 #include <framework/logging/Logger.h>
21 using namespace Eigen;
36 int NNWaveFitter::readNetworkData(
const string& xmlData)
38 using namespace boost::property_tree;
39 using boost::property_tree::ptree;
44 B2DEBUG(400,
"Reading xml");
47 read_xml(ss, propertyTree);
48 }
catch (
const ptree_error& e) {
49 B2ERROR(
"Failed to parse xml data: " << e.what());
50 }
catch (std::exception
const& ex) {
51 B2ERROR(
"STD excpetion " << ex.what() <<
" in parsing.");
57 string function_name = propertyTree.get<
string>(
"PMML.NeuralNetwork.<xmlattr>.functionName");
58 if (function_name !=
"classification") {
59 B2ERROR(
"This is incorrect network, expected multiclass classifier.");
62 B2DEBUG(400,
"Claasifier confirmed.");
64 string activationFunction = propertyTree.get<
string>(
"PMML.NeuralNetwork.<xmlattr>.activationFunction");
65 if (activationFunction !=
"rectifier") {
66 B2ERROR(
"Expected rectifier (relu) activation, found " << activationFunction <<
"instead.");
69 B2DEBUG(400,
"Activation confirmed.");
71 string waveFormType = propertyTree.get<
string>(
"PMML.MiningBuildTask.Training.Waveform");
72 B2DEBUG(400,
"Waveform set to " << waveFormType);
73 }
catch (
const ptree_error& e) {
74 B2ERROR(
"PropertyTree excpetion : " << e.what() <<
" in network check.");
80 string pathString =
"PMML.MiningBuildTask.NetworkParameters";
81 map<size_t, size_t> m_layer_sizes_map;
82 for (
const auto& layer_tag : propertyTree.get_child(pathString)) {
83 if (layer_tag.first ==
"<xmlattr>")
continue;
84 size_t layer_no = layer_tag.second.get<
size_t>(
"<xmlattr>.number");
85 size_t layer_size = layer_tag.second.get<
size_t>(
"<xmlattr>.size");
86 m_layer_sizes_map.insert(make_pair(layer_no, layer_size));
90 m_nLayers = m_layer_sizes_map.size();
91 m_layerSizes.resize(m_nLayers);
92 for (
size_t iLayer = 0; iLayer < m_nLayers; ++iLayer)
93 m_layerSizes[iLayer] = m_layer_sizes_map[iLayer];
94 for (
size_t iLayer = 1; iLayer < m_nLayers; ++iLayer) {
95 m_networkCoefs.insert(make_pair(iLayer, make_pair(
96 MatrixXd(m_layerSizes[iLayer], m_layerSizes[iLayer - 1]),
97 VectorXd(m_layerSizes[iLayer])
101 for (
size_t iLayer = 0; iLayer < m_nLayers; ++iLayer)
102 m_layerStates.push_back(VectorXd(m_layerSizes[iLayer]));
103 B2DEBUG(400,
"Network topology read.");
104 }
catch (
const ptree_error& e) {
105 B2ERROR(
"PropertyTree excpetion : " << e.what() <<
" when reading network topology.");
111 string pathString(
"PMML.MiningBuildTask.Training");
112 for (
const auto& param_tag : propertyTree.get_child(pathString)) {
113 if (param_tag.first ==
"Parameter") {
114 string valueString(param_tag.second.get<
string>(
"<xmlattr>.value"));
115 double low = param_tag.second.get<
double>(
"<xmlattr>.low");
116 double high = param_tag.second.get<
double>(
"<xmlattr>.high");
117 if (valueString ==
"amplitude") {
118 m_amplitudeBounds.first = low;
119 m_amplitudeBounds.second = high;
120 }
else if (valueString ==
"t0") {
121 m_timeShiftBounds.first = low * Unit::ns;
122 m_timeShiftBounds.second = high * Unit::ns;
123 }
else if (valueString ==
"tau") {
124 m_waveWidthBounds.first = low * Unit::ns;
125 m_waveWidthBounds.second = high * Unit::ns;
131 m_tauCoder.setBounds(m_amplitudeBounds.first, m_amplitudeBounds.second,
132 m_waveWidthBounds.first, m_waveWidthBounds.second);
133 B2DEBUG(400,
"Read parameter bounds.");
134 }
catch (
const ptree_error& e) {
135 B2ERROR(
"PropertyTree excpetion: " << e.what() <<
" when reading parameter bounds.");
141 string pathString(
"PMML.DataDictionary.DataField");
142 m_binCenters.resize(m_layerSizes[m_nLayers - 1]);
143 m_bins.resize(m_layerSizes[m_nLayers - 1] + 1);
144 for (
const auto& value_tag : propertyTree.get_child(pathString)) {
145 if (value_tag.first ==
"Value") {
146 size_t i = value_tag.second.get<
size_t>(
"<xmlattr>.value");
148 m_bins[0] = value_tag.second.get<
double>(
"<xmlattr>.lower") * Unit::ns;
149 m_bins[i] = value_tag.second.get<
double>(
"<xmlattr>.upper") * Unit::ns;
150 m_binCenters[i - 1] = value_tag.second.get<
double>(
"<xmlattr>.midpoint") * Unit::ns;
154 B2DEBUG(400,
"Outputs done.");
155 }
catch (
const ptree_error& e) {
156 B2ERROR(
"PropertyTree excpetion: " << e.what() <<
" when reading bin data.");
162 string pathString(
"PMML.NeuralNetwork");
163 for (
const auto& nl_tag : propertyTree.get_child(pathString)) {
164 if (nl_tag.first !=
"NeuralLayer")
continue;
165 B2DEBUG(400,
"Reading neural layers " << nl_tag.first <<
" " << nl_tag.second.size());
166 for (
const auto& neuron_tag : nl_tag.second) {
167 if (neuron_tag.first !=
"Neuron")
continue;
168 double bias = neuron_tag.second.get<
double>(
"<xmlattr>.bias");
169 string sid = neuron_tag.second.get<
string>(
"<xmlattr>.id");
170 size_t layer = stoi(sid.substr(0, 1));
171 size_t pos = stoi(sid.substr(2, sid.size()));
172 B2DEBUG(400,
"Reading neurons " << layer <<
"/" << pos <<
" bias: " << bias);
173 m_networkCoefs[layer].second(pos - 1) = bias;
174 for (
const auto& con_tag : neuron_tag.second) {
175 if (con_tag.first !=
"Con")
continue;
176 double weight = con_tag.second.get<
double>(
"<xmlattr>.weight");
177 string sid2 = con_tag.second.get<
string>(
"<xmlattr>.from");
178 size_t pos2 = stoi(sid2.substr(sid2.find(
'/') + 1, sid2.size()));
179 m_networkCoefs[layer].first(pos - 1, pos2 - 1) = weight;
180 B2DEBUG(400,
"Reading connections " << sid2 <<
" weight: " << weight);
184 B2DEBUG(400,
"Neurons done.");
185 }
catch (
const ptree_error& e) {
186 B2ERROR(
"PropertyTree excpetion: " << e.what() <<
" when reading neurons.");
192 bool NNWaveFitter::checkCoefficients(
const std::string& dumpname,
double tol)
195 ifstream dump(dumpname);
204 for (
size_t iLayer = 1; iLayer < m_nLayers; ++iLayer) {
210 for (
size_t iRow = 0; iRow < m_layerSizes[iLayer]; ++iRow) {
212 istringstream iline(line);
213 for (
size_t iCol = 0; iCol < m_layerSizes[iLayer - 1]; ++iCol) {
216 if (fabs(value - m_networkCoefs[iLayer].first(iRow, iCol)) > tol) {
217 B2DEBUG(90,
"Mismatch in weights in layer: " << iLayer
218 <<
" row: " << iRow <<
" col: " << iCol
219 <<
" C++: " << m_networkCoefs[iLayer].first(iRow, iCol)
220 <<
" Python: " << value
221 <<
" Diff: " << m_networkCoefs[iLayer].first(iRow, iCol) - value
232 istringstream iline(line);
233 for (
size_t iRow = 0; iRow < m_layerSizes[iLayer]; ++iRow) {
236 if (fabs(value - m_networkCoefs[iLayer].second(iRow)) > tol) {
237 B2DEBUG(90,
"Mismatch in intercepts in layer: " << iLayer
239 <<
" C++: " << m_networkCoefs[iLayer].second(iRow)
240 <<
" Python: " << value
241 <<
" Diff: " << m_networkCoefs[iLayer].second(iRow) - value
251 void NNWaveFitter::setNetwrok(
const string& xmlData)
253 if ((xmlData !=
"") && (readNetworkData(xmlData) == 0)) {
255 }
else m_isValid =
false;
260 NNWaveFitter::NNWaveFitter(
string xmlData)
263 if (xmlData !=
"") setNetwrok(xmlData);
267 std::shared_ptr<nnFitterBinData> NNWaveFitter::getFit(
const apvSamples& samples,
double tau)
271 if (!m_isValid)
return result;
273 copy(samples.begin(), samples.end(), &m_layerStates[0](0));
275 m_layerStates[0][samples.size()] = m_tauCoder.encodeTau(tau);
277 os <<
"Fitting with tau = " << tau <<
" encoded to " << m_tauCoder.encodeTau(tau) << endl
279 copy(samples.begin(), samples.end(), ostream_iterator<double>(os,
" "));
281 m_tauCoder.print(os);
282 B2DEBUG(100, os.str());
285 os <<
"Layer states: " << endl;
286 for (
size_t iLayer = 1; iLayer < m_nLayers; ++iLayer) {
287 m_layerStates[iLayer] = (
288 m_networkCoefs[iLayer].first * m_layerStates[iLayer - 1]
289 + m_networkCoefs[iLayer].second
291 os << m_layerStates[iLayer] << endl;
293 B2DEBUG(100, os.str());
297 m_layerStates[m_nLayers - 1] = softmax(m_layerStates[m_nLayers - 1]);
299 result->resize(m_layerStates[m_nLayers - 1].size());
300 for (
size_t i = 0; i < result->size(); ++i)
301 (*result)[i] = m_layerStates[m_nLayers - 1][i];
304 os <<
"Result: " << endl;
305 copy(result->begin(), result->end(), ostream_iterator<double>(os,
" "));
307 B2DEBUG(100, os.str());
Waveform generator This is a functor to calculate APV samples from waveform.
std::array< apvSampleBaseType, nAPVSamples > apvSamples
vector od apvSample BaseType objects
std::vector< double > nnFitterBinData
Vector of values defined for bins, such as bin times or bin probabilities.
double w_betaprime(double t)
Beta-prime waveform shape, x^alpha/(1+x)^beta.
Abstract base class for different kinds of events.