1 class Vector { 2 std::vector<double> k; 3 int N; 4 public: 5 explicit Vector(int size): k(size) { 6 N = size; 7 for (int i = 0; i < N; i++) 8 k[i] = 0.0; 9 } 10 11 inline int GetSize() const { return N; } 12 13 inline double& operator[] (int ix) { 14 CHECK(ix < N && ix >= 0); 15 return k[ix]; 16 } 17 18 inline const double& operator[] (int ix) const { 19 CHECK(ix < N && ix >= 0); 20 return k[ix]; 21 } 22 23 inline Vector operator+ (const Vector & other) const { 24 CHECK(other.GetSize() == N); 25 Vector ret(N); 26 for (int i = 0; i < N; i++) 27 ret[i] = k[i] + other[i]; 28 return ret; 29 } 30 31 inline Vector operator- (const Vector & other) const { 32 CHECK(other.GetSize() == N); 33 Vector ret(N); 34 for (int i = 0; i < N; i++) 35 ret[i] = k[i] - other[i]; 36 return ret; 37 } 38 39 inline Vector operator* (double factor) const { 40 Vector ret(N); 41 for (int i = 0; i < N; i++) 42 ret[i] = k[i] * factor; 43 return ret; 44 } 45 46 inline double DotProduct (const Vector & other) const { 47 CHECK(other.GetSize() == N); 48 double ret = 0.0; 49 for (int i = 0; i < N; i++) 50 ret += k[i] * other[i]; 51 return ret; 52 } 53 54 inline double Len2 () const { 55 double ret = 0.0; 56 for (int i = 0; i < N; i++) 57 ret += k[i] * k[i]; 58 return ret; 59 } 60 61 inline double Len () const { return sqrt(Len2()); } 62 63 string ToString () const { 64 char temp[1024] = "("; 65 for (int i = 0; i < N; i++) 66 sprintf(temp, "%s%s%.1lf", temp, i == 0 ? "" : ", ", k[i]); 67 return (std::string)temp + ")"; 68 } 69 70 inline void Copy(const Vector & from) { 71 CHECK(from.GetSize() == N); 72 for (int i = 0; i < N; i++) 73 k[i] = from[i]; 74 } 75 }; 76 77 class Matrix { 78 int M; 79 std::vector<double*> data; 80 public: 81 /* 82 * This matrix can grow its "N" i.e. width 83 * See http://en.wikipedia.org/wiki/Matrix_(mathematics) 84 */ 85 86 Matrix (int M_, int N) { 87 M = M_; 88 for (int i = 0; i < N; i++) { 89 IncN(); 90 } 91 } 92 93 ~Matrix() { 94 for (int i = 0; i < data.size(); i++) 95 delete [] data[i]; 96 } 97 98 inline int GetM() const { return M; } 99 100 inline int GetN() const { return data.size(); } 101 inline void IncN() { 102 double * k = new double[M]; 103 for (int i = 0; i < M; i++) 104 k[i] = 0.0; 105 data.push_back(k); 106 } 107 108 109 inline double& At(int i, int j) { 110 CHECK(i < M && i >= 0); 111 CHECK(j < data.size() && j >= 0); 112 // Note the reverse order of indices! 113 return data[j][i]; 114 } 115 116 inline const double& At(int i, int j) const { 117 CHECK(i < M && i >= 0); 118 CHECK(j < data.size() && j >= 0); 119 // Note the reverse order of indices! 120 return data[j][i]; 121 } 122 123 Vector MultiplyRight (const Vector & v) const { 124 int N = data.size(); 125 CHECK(v.GetSize() == N); 126 Vector ret(M); 127 for (int i = 0; i < M; i++) 128 for (int j = 0; j < N; j++) 129 ret[i] += v[j] * At(i,j); 130 return ret; 131 } 132 133 Vector MultiplyLeft (const Vector & v_to_transpose) const { 134 int N = data.size(); 135 CHECK(v_to_transpose.GetSize() == M); 136 Vector ret(N); 137 for (int i = 0; i < M; i++) 138 for (int j = 0; j < N; j++) 139 ret[j] += v_to_transpose[i] * At(i,j); 140 return ret; 141 } 142 143 string ToString() const { 144 string ret = ""; 145 for (int i = 0; i < M; i++) { 146 ret += "["; 147 for (int j = 0; j < GetN(); j++) { 148 char temp[128] = ""; 149 sprintf(temp, "%s%.1lf", j == 0 ? "" : ", ", At(i,j)); 150 ret += temp; 151 } 152 ret += "]\n"; 153 } 154 return ret; 155 } 156 }; 157 158 Vector EstimateParameters(const Matrix & perf_m, const Vector & stats_v, double rel_diff, int * iter_count = NULL) 159 { 160 /* 161 * Goal: find Vector<N> parameters: 162 * (perf_m * parameters - stats_v)^2 -> min 163 * With the following limitations: 164 * parameters[i] >= 0 165 * "Stop rule": 166 * for every i=0...M 167 * -> |stats_v[i] - (perf_m * parameters)[i]| < rel_diff * stats_v[i] 168 * Algorithm used is a gradient descend 169 */ 170 int N = perf_m.GetN(), M = perf_m.GetM(); 171 CHECK(stats_v.GetSize() == M); 172 Vector current(N); 173 174 // First, let's find out those lines in matrix having only one non-zero coefficient 175 // and if we have any, decrease the dimension of our matrix 176 { 177 int count_easy_param = 0; 178 std::vector<int> parameters_set(N); // N (line#) -> M (row#) of the only nonzero element; -1 if 0/2+ 179 for (int n = 0; n < N; n++) { 180 parameters_set[n] = -1; 181 } 182 // we may detect & assert unresolvable conflicts like the following 183 // |1| |1| 184 // |1| * p = |2| 185 // |1| |3| 186 187 // Find out those 0000*0000 lines 188 for (int m = 0; m < M; m++) { 189 int zero_id = -1; // -1 = not found, -2 = found twice, else - idx 190 for (int n = 0; n < N; n++) { 191 const double & m_n = perf_m.At(m,n); 192 if (m_n != 0.0) { 193 if (zero_id == -1) { 194 zero_id = n; 195 } else if (zero_id >= 0) { 196 zero_id = -2; 197 break; 198 } 199 } 200 } 201 if (zero_id >= 0) { 202 CHECK(parameters_set[zero_id] == -1); 203 parameters_set[zero_id] = m; 204 count_easy_param++; 205 current[zero_id] = stats_v[m] / perf_m.At(m, zero_id); 206 } 207 } 208 209 if (count_easy_param > 0) { 210 //printf("tmp = %s\n", current.ToString().c_str()); 211 212 //printf("\n\nDecreasing the dimensions!\n"); 213 std::vector<int> new_m_to_old(M - count_easy_param), 214 new_n_to_old(N - count_easy_param); 215 for (int m = 0; m < M - count_easy_param; m++) { 216 // see increments later 217 new_m_to_old[m] = m; 218 } 219 int cur_n = 0; 220 for (int n = 0; n < N; n++) { 221 if (parameters_set[n] == -1) { 222 new_n_to_old[cur_n] = n; 223 cur_n++; 224 } else { 225 for (int m = parameters_set[n]; m < M - count_easy_param; m++) { 226 new_m_to_old[m]++; 227 } 228 } 229 } 230 231 Vector auto_stats = perf_m.MultiplyRight(current), // we have these stats for sure ... 232 diff_stats = stats_v - auto_stats; // ... and we need to get these stats more 233 234 // Formulate the sub-problem (sub-matrix & sub-stats) 235 Matrix new_m(M - count_easy_param, N - count_easy_param); 236 Vector new_stats(M - count_easy_param); 237 for (int m = 0; m < M - count_easy_param; m++) { 238 new_stats[m] = diff_stats[new_m_to_old[m]]; 239 CHECK(new_stats[m] >= 0.0); 240 for (int n = 0; n < N - count_easy_param; n++) 241 new_m.At(m,n) = perf_m.At(new_m_to_old[m], new_n_to_old[n]); 242 } 243 244 // Solve the submatrix and put the results back 245 Vector new_param = EstimateParameters(new_m, new_stats, rel_diff, iter_count); 246 for (int n = 0; n < N - count_easy_param; n++) { 247 current[new_n_to_old[n]] = new_param[n]; 248 } 249 return current; 250 } 251 } 252 253 bool stop_condition = false; 254 double prev_distance = stats_v.Len2(); 255 256 //printf("Initial distance = %.1lf\n", prev_distance); 257 while (stop_condition == false) { 258 (*iter_count)++; 259 Vector m_by_curr(M); 260 m_by_curr.Copy(perf_m.MultiplyRight(current)); 261 Vector derivative(N); // this is actually "-derivative/2" :-) 262 derivative.Copy(perf_m.MultiplyLeft(stats_v - m_by_curr)); 263 264 if (derivative.Len() > 1000.0) { 265 derivative = derivative * (1000.0 / derivative.Len()); 266 } 267 268 // Descend according to the gradient 269 { 270 Vector new_curr(N); 271 double step = 1024.0; 272 double new_distance; 273 for (int i = 0; i < N; i++) { 274 if (current[i] + derivative[i] * step < 0.0) { 275 step = - current[i] / derivative[i]; 276 } 277 } 278 do { 279 new_curr = current + derivative*step; 280 step /= 2.0; 281 new_distance = (perf_m.MultiplyRight(new_curr) - stats_v).Len2(); 282 if (step < 0.00001) { 283 stop_condition = true; 284 } 285 (*iter_count)++; 286 } while (new_distance >= prev_distance && !stop_condition); 287 288 prev_distance = new_distance; 289 current = new_curr; 290 //printf ("Dist=%.1lf, cur=%s\n", new_distance, current.ToString().c_str()); 291 } 292 293 // Check stop condition 294 if (stop_condition == false) 295 { 296 stop_condition = true; 297 Vector stats_est(M); 298 stats_est.Copy(perf_m.MultiplyRight(current)); 299 for (int i = 0; i < M; i++) 300 if (fabs(stats_v[i] - stats_est[i]) > rel_diff * stats_v[i]) 301 stop_condition = false; 302 } 303 } 304 305 return current; 306 } 307