cMHN 1.0
C++ library for learning MHNs with pRC
learn_theta.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: BSD-2-Clause
2
3#ifndef cMHN_NONTT_LEARN_THETA_H
4#define cMHN_NONTT_LEARN_THETA_H
5
6#include <iostream>
7#include <map>
8#include <string>
9#include <tuple>
10
16
17#include <prc.hpp>
18
19namespace cMHN::nonTT
20{
51 template<class T, pRC::Size D, class S>
52 std::tuple<pRC::Tensor<T, D, D>,
53 std::map<std::string, std::string>,
54 std::map<std::string, double>>
55 learnTheta(pRC::Tensor<T, D, D> const &theta,
56 std::string const &header, std::string const &output,
57 std::map<S, T> const &pD, cMHN::Score<T> const &Score,
58 cMHN::Regulator<T, D> const &Regulator, T const &toleranceOptimizer,
59 T const &toleranceSolverP, T const &toleranceSolverQ)
60 {
61 auto tempTheta = theta;
62
63 auto score = pRC::zero<T>();
64
65 pRC::Index at_iter = 0;
66
67 pRC::Float<64> startTime = pRC::getTimeInSeconds();
68
69 std::map<std::string, double> logInfoNumbers{{"Score", score()},
70 {"Iterations", at_iter},
71 {"Time", pRC::getTimeInSeconds()() - startTime()},
72 {"Lambda", Regulator.lambda()()}};
73
74 std::map<std::string, std::string> logInfoNames{
75 {"Score Name", Score.name()}, {"Regulator Name", Regulator.name()}};
76
77 writeTheta(output, header, tempTheta, logInfoNames, logInfoNumbers);
78
79 std::cout << "cMHN learning started (nonTT):" << std::endl;
80 std::cout << "\tScore Name:\t" << logInfoNames["Score Name"]
81 << std::endl;
82 std::cout << "\tRegulator Name:\t" << logInfoNames["Regulator Name"]
83 << std::endl
84 << std::endl;
85
86 // use a random initial pInit
87 pRC::SeedSequence seq(8, 16);
88 pRC::RandomEngine rng(seq);
89 pRC::GaussianDistribution<pRC::Float<>> dist;
90 auto pInit = eval(expand(pRC::makeConstantSequence<pRC::Size, D, 2>(),
91 [&](auto const... Ns)
92 {
93 return pRC::random<pRC::Tensor<T, Ns...>>(rng, dist);
94 }));
95 pInit = pInit / norm(pInit);
96
97 tempTheta = pRC::optimize<pRC::Optimizer::BFGS<>>(
98 tempTheta,
99 [&output, &at_iter, &score, &pD, &Score, &Regulator, &pInit,
100 &toleranceOptimizer, &toleranceSolverP,
101 &toleranceSolverQ](auto const &tempTheta, auto &g)
102 {
103 MHNOperator<T, D> op(tempTheta);
104
105 auto const pTheta =
106 ::cMHN::calculatePTheta(op, pInit, toleranceSolverP);
107
108 // use last pTheta as next pInit
109 pInit = pTheta;
110
111 g = pRC::zero();
112 score = pRC::zero();
113 for(auto const &[k, v] : pD)
114 {
115 score += Score.pointwiseScore(v, pTheta(k));
116 }
117
118 // calculate g = dS/dtheta
119 auto rhs =
120 eval(expand(pRC::makeConstantSequence<pRC::Size, D, 2>(),
121 [](auto const... Ns)
122 {
123 return pRC::zero<pRC::Tensor<T, Ns...>>();
124 }));
125
126 for(auto const &[k, v] : pD)
127 {
128 rhs(k) = Score.pointwiseDSDP(v, pTheta(k));
129 }
130
131 auto q = pRC::solve<pRC::Solver::GMRES<>,
132 pRC::Operator::Transform::Transpose>(op, rhs,
133 pRC::zero<decltype(rhs)>(), toleranceSolverQ);
134
135 // this follows appendix C of Rudi's Thesis
136 for(pRC::Index i = 0; i < D; ++i)
137 {
138 auto r = hadamardProduct(q,
139 cMHN::nonTT::applyDerivative(op, pTheta, i));
140
141 pRC::range<pRC::Context::CompileTime, D>(
142 [&](auto const j)
143 {
144 if(i == j)
145 {
146 g(i, j) = pRC::reduce<pRC::Add>(r)();
147 }
148 else
149 {
150 g(i, j) =
151 pRC::reduce<pRC::Add>(pRC::chip<j>(r, 1))();
152 }
153 });
154 }
155
156 // add regularization
157 // signs b/c pRC::Optimmizer::BFGS minimizes the score function
158 g = Regulator.grad(tempTheta) - g;
159 return Regulator.score(tempTheta) - score;
160 },
161 [&output, &header, &score, &at_iter, &startTime, &logInfoNames,
162 &logInfoNumbers](auto const &tempTheta)
163 {
164 logInfoNumbers["Iterations"] = at_iter;
165 logInfoNumbers["Score"] = score();
166 logInfoNumbers["Time"] =
167 pRC::getTimeInSeconds()() - startTime();
168
169 std::cout << "cMHN learning in progress (nonTT):" << std::endl;
170 std::cout << std::defaultfloat;
171 std::cout << "\tIteration:\t" << logInfoNumbers["Iterations"]
172 << std::endl;
173 std::cout << std::scientific;
174 std::cout << "\tLambda:\t\t" << logInfoNumbers["Lambda"]
175 << std::endl;
176 std::cout << "\tScore:\t\t" << logInfoNumbers["Score"]
177 << std::endl;
178 std::cout << "\tTime:\t\t" << logInfoNumbers["Time"]
179 << std::endl;
180 std::cout << std::defaultfloat;
181
182 writeTheta(output, header, tempTheta, logInfoNames,
183 logInfoNumbers);
184 at_iter++;
185 },
186 toleranceOptimizer);
187
188 return std::make_tuple(tempTheta, logInfoNames, logInfoNumbers);
189 }
190}
191
192#endif // cMHN_NONTT_LEARN_THETA_H
pRC::Size const D
Definition: CalculatePThetaTests.cpp:9
Class storing all relevant information for a regulator.
Definition: regulator.hpp:30
auto & lambda()
Definition: regulator.hpp:58
auto grad(pRC::Tensor< T, D, D > const &theta) const
Definition: regulator.hpp:53
auto name() const
Definition: regulator.hpp:68
auto score(pRC::Tensor< T, D, D > const &theta) const
Definition: regulator.hpp:48
Class storing all relevant information for a score.
Definition: score.hpp:27
auto pointwiseScore(T const &pDE, T const &pThetaE) const
Definition: score.hpp:44
auto name() const
Definition: score.hpp:54
auto pointwiseDSDP(T const &pDE, T const &pThetaE) const
Definition: score.hpp:49
Class storing an MHN operator represented by a theta matrix (for non TT calculations)
Definition: mhn_operator.hpp:23
pRC::Float<> T
Definition: externs_nonTT.hpp:1
Definition: learn_theta.hpp:20
static constexpr auto applyDerivative(MHNOperator< T1, D > const &op, pRC::Tensor< T2, Ns... > const &x, pRC::Index const &i)
apply the derivative of an MHN Q wrt to theta_ii to a vector x
Definition: mhn_operator.hpp:71
std::tuple< pRC::Tensor< T, D, D >, std::map< std::string, std::string >, std::map< std::string, double > > learnTheta(pRC::Tensor< T, D, D > const &theta, std::string const &header, std::string const &output, std::map< S, T > const &pD, cMHN::Score< T > const &Score, cMHN::Regulator< T, D > const &Regulator, T const &toleranceOptimizer, T const &toleranceSolverP, T const &toleranceSolverQ)
Optimizes an MHN represented by a theta matrix to best describe a given data distribution.
Definition: learn_theta.hpp:55
X calculatePTheta(nonTT::MHNOperator< T, D > const &op, X const &pInit, T const &toleranceSolver)
Calculates the vector pTheta given a nonTT MHN Operator and a tolerance.
Definition: calculate_pTheta.hpp:33
static auto writeTheta(std::string const &filename, std::string const &header, pRC::Tensor< T, D, D > const &theta, std::map< std::string, std::string > const &logInfoNames={}, std::map< std::string, double > const &logInfoNumbers={})
Writes a theta matrix to file, including additional logging information at the bottom.
Definition: write_theta.hpp:29