Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
MultipleLinearRegression.h
Go to the documentation of this file.
1
11
12#pragma once
13
14#ifndef __MULTIPLE_LINEAR_REGRESSION_H__
15#define __MULTIPLE_LINEAR_REGRESSION_H__
16
17#include <Eigen/Eigen>
18#include <cmath>
19#include <vector>
20
21namespace Utils
22{
23
24// WARNING: it doesn't let it go lower than the minimum value, if the prediction is lower, the min value is returned!
25// set "TrueLinearRegression" to true if you want to use the real linear regression
27{
28 public:
30
31 MultipleLinearRegression(const Eigen::VectorXd &initialWeights, double initialBias = 0.0, bool initialTrueLinearRegression = false)
32 : W(initialWeights), b(initialBias), trueLinearRegression(initialTrueLinearRegression)
33 {
34 }
35
36 void SetSamples(const std::vector<std::vector<double>> &x, const std::vector<double> &y)
37 {
38 assert(x.size() == y.size());
39 assert(!x.empty());
40
41 if (x.empty() || y.empty())
42 return;
43
44 const size_t n = std::min(x.size(), y.size());
45 const size_t m = x[0].size();
46
47 // if x is extended with 1 (for the bias term), then it's just a matrix thing, like this:
48 // W = (X^t * X)^-1 * X^t * y
49 // but from there the real W must be extracted and also b is going to be saved separately
50
51 Eigen::MatrixXd X;
52 X.resize(n, m + 1);
53 X.col(0).setOnes();
54
55 for (size_t i = 0; i < n; ++i)
56 for (size_t j = 1; j <= m; ++j)
57 X(i, j) = x[i][j - 1];
58
59 Eigen::VectorXd Y;
60 Y.resize(n);
61 for (size_t i = 0; i < n; ++i)
62 Y(i) = y[i];
63
64 Eigen::MatrixXd Wa;
65
66 /*
67 Eigen::MatrixXd Xt = X.transpose();
68 Eigen::MatrixXd XtX = Xt * X;
69
70 if (abs(XtX.determinant()) < 1E-10)
71 Wa = XtX.completeOrthogonalDecomposition().pseudoInverse() * Xt * Y;
72 else
73 Wa = XtX.inverse() * Xt * Y;
74 */
75
76 if (alpha > 0.)
77 {
78 // Ridge (L2) regularization: minimize ||X * w - Y||^2 + lambda * ||w||^2.
79 // The penalty strength is derived from the data so it adapts to the
80 // (unscaled) feature magnitudes: lambda = alpha * trace(X^t * X) / m,
81 // using only the feature columns (the bias column is left unregularized).
82 // It is solved by augmenting the system with sqrt(lambda) * I rows (and
83 // zeros in Y), which keeps the well-conditioned QR solve instead of
84 // forming X^t * X.
85 double trace = 0.;
86 for (size_t j = 1; j <= m; ++j)
87 trace += X.col(j).squaredNorm();
88
89 const double lambda = (m > 0) ? alpha * trace / static_cast<double>(m) : 0.;
90
91 Eigen::MatrixXd Xa(n + m, m + 1);
92 Xa.topRows(n) = X;
93 Xa.bottomRows(m).setZero();
94
95 const double sqrtLambda = std::sqrt(lambda);
96 for (size_t j = 0; j < m; ++j)
97 Xa(n + j, j + 1) = sqrtLambda;
98
99 Eigen::VectorXd Ya(n + m);
100 Ya.head(n) = Y;
101 Ya.tail(m).setZero();
102
103 Wa = Xa.colPivHouseholderQr().solve(Ya);
104 }
105 else
106 Wa = X.colPivHouseholderQr().solve(Y);
107
108 b = Wa(0);
109 W.resize(m);
110 for (size_t i = 0; i < m; ++i)
111 W(i) = Wa(i + 1);
112 // std::cout << "W: " << W << std::endl;
113 // std::cout << "b: " << b << std::endl;
114 }
115
116 double Predict(const std::vector<double> &x) const
117 {
118 assert(x.size() == static_cast<size_t>(W.rows()));
119
120 if (x.size() != static_cast<size_t>(W.size()))
121 return 0.;
122
123 Eigen::RowVectorXd xRow;
124 xRow.resize(x.size());
125 for (size_t i = 0; i < x.size(); ++i)
126 xRow(i) = x[i];
127
128 const auto val = xRow * W + b;
129
130 if (trueLinearRegression)
131 return val;
132
133 const auto m = 1E-12;
134 if (val < m)
135 return m;
136
137 return val;
138 }
139
141 {
142 trueLinearRegression = reg;
143 }
144
145 // Ridge (L2) regularization factor (alpha). The effective penalty is derived
146 // from the data as alpha * trace(X^t * X) / m, so it adapts to the (unscaled)
147 // feature magnitudes. Set to 0 to disable. Applied before SetSamples.
148 // A small value such as 1e-6 stabilizes collinear/rank-deficient fits without
149 // materially biasing the weights.
150 void SetRegularization(double reg)
151 {
152 alpha = reg;
153 }
154
155 const Eigen::VectorXd &GetWeights() const
156 {
157 return W;
158 }
159
160 double GetBias() const
161 {
162 return b;
163 }
164
166 {
167 return trueLinearRegression;
168 }
169
170 double GetRegularization() const
171 {
172 return alpha;
173 }
174
175 private:
176 Eigen::VectorXd W;
177 double b = 0.;
178
179 bool trueLinearRegression = false;
180 double alpha = 1e-6;
181};
182
183} // namespace Utils
184
185#endif // __MULTIPLE_LINEAR_REGRESSION_H__
void SetSamples(const std::vector< std::vector< double > > &x, const std::vector< double > &y)
double Predict(const std::vector< double > &x) const
MultipleLinearRegression(const Eigen::VectorXd &initialWeights, double initialBias=0.0, bool initialTrueLinearRegression=false)
const Eigen::VectorXd & GetWeights() const
Portable dense contraction kernels.