1 /**
2 * UGENE - Integrated Bioinformatics Tools.
3 * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4 * http://ugene.net
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #include "SiteconAlgorithm.h"
23
24 #include <QFile>
25
26 #include <U2Core/DNAAlphabet.h>
27 #include <U2Core/DNATranslation.h>
28 #include <U2Core/Log.h>
29 #include <U2Core/MultipleSequenceAlignment.h>
30 #include <U2Core/TextUtils.h>
31 #include <U2Core/U2OpStatusUtils.h>
32
33 #include "DIPropertiesSitecon.h"
34 #include "SiteconMath.h"
35
36 namespace U2 {
37
operator !=(const SiteconModel & model) const38 bool SiteconModel::operator!=(const SiteconModel &model) const {
39 bool eq = true;
40 if (matrix.size() != model.matrix.size()) {
41 eq = false;
42 }
43 for (int i = 0; i < matrix.size() && eq; i++) {
44 PositionStats pos = matrix[i];
45 if (pos.size() != model.matrix[i].size()) {
46 eq = false;
47 }
48 for (int j = 0; j < pos.size() && eq; j++) {
49 DiStat ds1 = pos[j];
50 DiStat ds2 = model.matrix[i][j];
51 for (int k = 0; k < 16 && eq; k++) {
52 eq = ds1.prop->original[k] == ds2.prop->original[k] && ds1.prop->normalized[k] == ds2.prop->normalized[k];
53 }
54 eq = eq && ds1.average == ds2.average && ds1.sdeviation == ds2.sdeviation;
55 eq = eq && ds1.weighted == ds2.weighted && ds1.prop->average == ds2.prop->average && ds1.prop->sdeviation == ds2.prop->sdeviation;
56 eq = eq && ds1.prop->keys == ds2.prop->keys;
57 }
58 }
59 for (int i = 0; i < err1.size() && eq; i++) {
60 eq = err1[i] == model.err1[i] && err2[i] == model.err2[i];
61 }
62 return !eq;
63 }
64
calculateDispersionAndAverage(const MultipleSequenceAlignment & ma,const SiteconBuildSettings & config,TaskStateInfo & ts)65 QVector<PositionStats> SiteconAlgorithm::calculateDispersionAndAverage(const MultipleSequenceAlignment &ma, const SiteconBuildSettings &config, TaskStateInfo &ts) {
66 const QList<DiPropertySitecon *> &props = config.props;
67 assert(!props.isEmpty());
68 QVector<PositionStats> matrix;
69 int N = ma->getNumRows();
70 for (int i = 0, n = ma->getLength() - 1; i < n && !ts.cancelFlag; i++) { // for every di-nucl
71 PositionStats posResult;
72 foreach (DiPropertySitecon *p, props) { // for every property
73 qreal average = 0; // average in a column
74 foreach (const MultipleSequenceAlignmentRow &row, ma->getMsaRows()) { // collect di-position stat for all sequence in alignment
75 average += p->getOriginal(row->charAt(i), row->charAt(i + 1));
76 }
77 average /= N;
78
79 qreal dispersion = 0; // dispersion in a column
80 for (int j = 0; j < ma->getNumRows(); j++) { // collect di-position stat for all sequence in alignment
81 const MultipleSequenceAlignmentRow row = ma->getMsaRow(j);
82 char c1 = row->charAt(i);
83 char c2 = row->charAt(i + 1);
84 qreal v = p->getOriginal(c1, c2);
85 dispersion += (average - v) * (average - v);
86 }
87 dispersion /= (N - 1);
88 qreal sdeviation = sqrt(dispersion);
89
90 posResult.append(DiStat(p, sdeviation, average));
91 }
92 matrix.append(posResult);
93 }
94 if (ts.cancelFlag || ts.hasError()) {
95 matrix.clear();
96 return matrix;
97 }
98 assert(matrix.size() == ma->getLength() - 1);
99 return matrix;
100 }
101
calculatePSum(const char * seq,int len,const QVector<PositionStats> & normalizedMatrix,const SiteconBuildSettings & config,qreal devThreshold,DNATranslation * complMap)102 qreal SiteconAlgorithm::calculatePSum(const char *seq, int len, const QVector<PositionStats> &normalizedMatrix, const SiteconBuildSettings &config, qreal devThreshold, DNATranslation *complMap) {
103 Q_UNUSED(config);
104 assert(len == config.windowSize);
105 bool complement = complMap != nullptr;
106 QByteArray complMapper = complement ? complMap->getOne2OneMapper() : QByteArray();
107 qreal pSum = 0.0f;
108 qreal sdevDeltasSum = 0.0f;
109 for (int i = 0; i < len - 1; i++) {
110 char c1 = seq[i];
111 char c2 = seq[i + 1];
112 if (complement) {
113 char origC2 = c2;
114 c2 = complMapper[uchar(c1)];
115 c1 = complMapper[uchar(origC2)];
116 }
117 const PositionStats &posProps = complement ? normalizedMatrix[len - 1 - 1 - i] : normalizedMatrix[i];
118 assert(posProps.size() == config.props.size());
119 for (int k = 0, nprops = posProps.size(); k < nprops; k++) {
120 const DiStat &ds = posProps[k];
121 if (ds.sdeviation < devThreshold && ds.weighted) {
122 sdevDeltasSum += 1.0 / (ds.sdeviation + 0.1);
123
124 if (c1 == 'N' || c2 == 'N') {
125 continue;
126 }
127
128 qreal f = ds.prop->getNormalized(c1, c2);
129 qreal expPart = (ds.average - f) / (ds.sdeviation + 0.1f);
130 qreal p = exp((-1) * expPart * expPart) / (ds.sdeviation + 0.1f);
131 pSum += p;
132 }
133 }
134 }
135
136 if (sdevDeltasSum == 0.0f) {
137 assert(pSum == 0.0f);
138 return 0;
139 }
140 pSum /= sdevDeltasSum;
141 return pSum;
142 }
143
calculateFirstTypeError(const MultipleSequenceAlignment & ma,const SiteconBuildSettings & s,TaskStateInfo & ts)144 QVector<qreal> SiteconAlgorithm::calculateFirstTypeError(const MultipleSequenceAlignment &ma, const SiteconBuildSettings &s, TaskStateInfo &ts) {
145 qreal devThresh = critchi(s.chisquare, s.numSequencesInAlignment - 2) / (s.numSequencesInAlignment - 1);
146
147 QVector<qreal> scores;
148 // 1. Exclude 1 sequence for MSA and create Dis/Ave matrix on whats left in MSA
149 // 2. Calculate score for excluded sequence
150 // 3. Distribute percentage for all scores
151
152 U2OpStatus2Log os;
153 int maLen = ma->getLength();
154 for (int i = 0; i < ma->getNumRows() && !ts.cancelFlag; i++) {
155 const MultipleSequenceAlignmentRow row = ma->getMsaRow(i);
156 MultipleSequenceAlignment subMA = ma->getCopy();
157 subMA->removeRow(i, os);
158 QVector<PositionStats> matrix = calculateDispersionAndAverage(subMA, s, ts);
159 QVector<PositionStats> normalizedMatrix = normalize(matrix, s);
160 calculateWeights(subMA, normalizedMatrix, s, true, ts);
161 qreal p = calculatePSum(row->toByteArray(os, maLen), maLen, normalizedMatrix, s, devThresh);
162 scores.append(p);
163 }
164 QVector<qreal> res(100, 0);
165 if (ts.cancelFlag) {
166 return res;
167 }
168 for (int i = 0; i < 100; i++) {
169 int numScoresLowerThanI = 0;
170 foreach (qreal score, scores) {
171 if (score * 100 < i) {
172 numScoresLowerThanI++;
173 }
174 }
175 res[i] = numScoresLowerThanI / qreal(scores.size());
176 }
177 return res;
178 }
179
calculateSecondTypeError(const QVector<PositionStats> & matrix,const SiteconBuildSettings & settings,TaskStateInfo & si)180 QVector<qreal> SiteconAlgorithm::calculateSecondTypeError(const QVector<PositionStats> &matrix,
181 const SiteconBuildSettings &settings,
182 TaskStateInfo &si) {
183 qreal devThresh = critchi(settings.chisquare, settings.numSequencesInAlignment - 1) / settings.numSequencesInAlignment;
184
185 qsrand(settings.randomSeed);
186 QByteArray randomSequence = generateRandomSequence(settings.acgtContent, settings.secondTypeErrorCalibrationLen, si);
187
188 int dProgress = 100 - si.progress;
189 int nuclsPerProgress = randomSequence.size() / dProgress;
190 int progressI = nuclsPerProgress;
191
192 QVector<PositionStats> normalizedMatrix = normalize(matrix, settings);
193 QVector<int> hitsPerScore(100, 0);
194 const char *seq = randomSequence.constData();
195 for (int i = 0; i < randomSequence.size() - (settings.windowSize - 1) && !si.cancelFlag; i++) {
196 const char *subseq = seq + i;
197 qreal psum = calculatePSum(subseq, settings.windowSize, normalizedMatrix, settings, devThresh);
198 hitsPerScore[qRound(psum * 100)]++;
199 if (--progressI == 0) {
200 progressI = nuclsPerProgress;
201 si.progress++;
202 }
203 }
204
205 QVector<qreal> errorPerScore(100, 0);
206 int totalHits = 0;
207 for (int i = 100; --i >= 0;) {
208 totalHits += hitsPerScore[i];
209 qreal err = totalHits / (qreal)(settings.secondTypeErrorCalibrationLen - settings.windowSize + 1);
210 assert(err <= 1);
211 errorPerScore[i] = err;
212 }
213 return errorPerScore;
214 }
215
normalize(const QVector<PositionStats> & matrix,const SiteconBuildSettings & settings)216 QVector<PositionStats> SiteconAlgorithm::normalize(const QVector<PositionStats> &matrix, const SiteconBuildSettings &settings) {
217 Q_UNUSED(settings); // TODO: remove this arg
218
219 // calculate scale average and deviation
220 // normalize initial model by scale:
221 // model_ave = (model_ave - scale_ave) / scale_dev
222 // model_dev = model_dev / scale_dev
223
224 // normalize initial matrix;
225 QVector<PositionStats> normMatrix;
226 for (int i = 0, n = matrix.size(); i < n; i++) {
227 const PositionStats &list = matrix[i];
228 PositionStats normList;
229 for (int j = 0, m = list.size(); j < m; j++) {
230 const DiStat &dsModel = list[j];
231 DiStat normDS;
232 normDS.prop = dsModel.prop;
233 normDS.average = (dsModel.average - dsModel.prop->average) / (dsModel.prop->sdeviation);
234 normDS.sdeviation = dsModel.sdeviation / (dsModel.prop->sdeviation);
235 normDS.weighted = dsModel.weighted;
236 normList.append(normDS);
237 }
238 normMatrix.append(normList);
239 }
240 return normMatrix;
241 }
242
calculateACGTContent(const MultipleSequenceAlignment & ma,SiteconBuildSettings & bs)243 void SiteconAlgorithm::calculateACGTContent(const MultipleSequenceAlignment &ma, SiteconBuildSettings &bs) {
244 assert(ma->getAlphabet()->isNucleic());
245 bs.acgtContent[0] = bs.acgtContent[1] = bs.acgtContent[2] = bs.acgtContent[3] = 0;
246 int maLen = ma->getLength();
247 int total = ma->getNumRows() * ma->getLength();
248 foreach (const MultipleSequenceAlignmentRow &row, ma->getMsaRows()) {
249 for (int i = 0; i < maLen; i++) {
250 char c = row->charAt(i);
251 if (c == 'A') {
252 bs.acgtContent[0]++;
253 } else if (c == 'C') {
254 bs.acgtContent[1]++;
255 } else if (c == 'G') {
256 bs.acgtContent[2]++;
257 } else if (c == 'T') {
258 bs.acgtContent[3]++;
259 } else {
260 total--;
261 }
262 }
263 }
264 for (int i = 0; i < 4; i++) {
265 bs.acgtContent[i] = qRound(bs.acgtContent[i] * 100. / total);
266 }
267 }
268
generateRandomSequence(const int * acgtContent,int seqLen,TaskStateInfo &)269 QByteArray SiteconAlgorithm::generateRandomSequence(const int *acgtContent, int seqLen, TaskStateInfo &) {
270 QByteArray randomSequence;
271 randomSequence.reserve(seqLen);
272
273 int aPercentRange = acgtContent[0];
274 int cPercentRange = aPercentRange + acgtContent[1];
275 int gPercentRange = cPercentRange + acgtContent[2];
276
277 assert(gPercentRange + acgtContent[3] > 0);
278
279 for (int i = 0; i < seqLen; i++) {
280 int r = qrand();
281 qreal perc = 100 * (r / (qreal)RAND_MAX);
282 char c = 'T';
283 if (perc <= aPercentRange) {
284 c = 'A';
285 } else if (perc <= cPercentRange) {
286 c = 'C';
287 } else if (perc <= gPercentRange) {
288 c = 'G';
289 }
290 randomSequence.append(c);
291 }
292 return randomSequence;
293 }
294
295 #define MAX_WEIGHTS_ALG2 6
296
297 typedef QVector<QVector<qreal>> PWVector;
298
299 //#define _SITECON_ALG_DEBUG_
300
301 #ifdef _SITECON_ALG_DEBUG_
302
dumpWeights(const QString & url,const PWVector & weights,const SiteconBuildSettings & settings)303 static void dumpWeights(const QString &url, const PWVector &weights, const SiteconBuildSettings &settings) {
304 QVector<QByteArray> wght(settings.props.size());
305 for (int j = 0; j < settings.props.size(); j++) {
306 const DiPropertySitecon *p = settings.props[j];
307 QString name = p->keys.value("PV");
308 QString index = p->keys.value("MI");
309 wght[j].append(index).append(";").append(name);
310 }
311
312 wght.resize(settings.props.size());
313 for (int i = 0; i < weights.size(); i++) {
314 const QVector<qreal> &posWeights = weights[i];
315 for (int j = 0; j < settings.props.size(); j++) {
316 qreal w = posWeights[j];
317 const char *ali = TextUtils::getLineOfSpaces(wght[j].length() < 25 ? 25 - wght[j].length() : (2 - wght[j].length() % 2));
318 wght[j].append(";").append(ali).append(QString::number(w, 'f', 2));
319 }
320 }
321
322 QFile file(url);
323 file.open(QIODevice::WriteOnly | QIODevice::Truncate);
324 foreach (const QByteArray &w, wght) {
325 file.write(w);
326 file.write("\n");
327 }
328 file.close();
329 }
330 #endif
331
calculateWeights(const MultipleSequenceAlignment & ma,QVector<PositionStats> & origMatrix,const SiteconBuildSettings & settings,bool matrixIsNormalized,TaskStateInfo & si)332 int SiteconAlgorithm::calculateWeights(const MultipleSequenceAlignment &ma, QVector<PositionStats> &origMatrix, const SiteconBuildSettings &settings, bool matrixIsNormalized, TaskStateInfo &si) {
333 int modelSize = settings.windowSize - 1;
334 if (settings.weightAlg == SiteconWeightAlg_None) {
335 for (int i = 0; i < modelSize; i++) {
336 PositionStats &ps = origMatrix[i];
337 for (int j = 0; j < ps.size(); j++) {
338 DiStat &ds = ps[j];
339 ds.weighted = true;
340 }
341 }
342 return settings.props.size();
343 }
344
345 assert(settings.weightAlg == SiteconWeightAlg_Alg2);
346
347 // to calculate weights by algorithm2
348 // 1. generate ~modelLen*seqNums + 10 sequence with ACGT content == model content and
349 // 1 for every prop calculate average weight
350 // 2. for every pos select max weight per props only for di-nucls in model
351 // 3. calculate diff = W2_max - W1_ave
352 // 4. mark up to 6 props per pos as weighted with max-diffs < chisquare
353
354 assert(ma->getLength() == settings.windowSize);
355 assert(origMatrix.size() == settings.windowSize - 1);
356
357 // clear weights data
358 for (int i = 0; i < origMatrix.size(); i++) {
359 PositionStats &ps = origMatrix[i];
360 for (int j = 0; j < ps.size(); j++) {
361 ps[j].weighted = false;
362 }
363 }
364
365 // normalize matrix if needed
366 QVector<PositionStats> normMatrix = origMatrix;
367 if (!matrixIsNormalized) {
368 normMatrix = normalize(origMatrix, settings);
369 }
370
371 qreal devThreshold = critchi(settings.chisquare, settings.numSequencesInAlignment - 1) / settings.numSequencesInAlignment;
372
373 // Part1
374 // 1. compute props ave on random sequence
375 int rndSeqLen = modelSize * ma->getNumRows() + 10;
376 QByteArray rndSeqArray = generateRandomSequence(settings.acgtContent, rndSeqLen, si);
377 const char *rndSeq = rndSeqArray.constData();
378
379 // init weights with default val
380 PWVector aveWeightMatrix(modelSize);
381 PWVector maxWeightMatrix(modelSize);
382 for (int i = 0; i < modelSize; i++) {
383 aveWeightMatrix[i].fill(0, settings.props.size());
384 maxWeightMatrix[i].fill(0, settings.props.size());
385 }
386
387 // sum all psums for nSamples and create average vals per (pos, prop)
388 int nSamples = rndSeqLen - modelSize;
389 for (int i = 0; i < nSamples && !si.cancelFlag; i++) {
390 for (int pos = 0; pos < modelSize; pos++) {
391 char c1 = rndSeq[i + pos];
392 char c2 = rndSeq[i + pos + 1];
393 if (c1 == 'N' || c2 == 'N') {
394 continue;
395 }
396 QVector<qreal> &posWeights = aveWeightMatrix[pos];
397 PositionStats &ps = normMatrix[pos];
398 for (int j = 0; j < ps.size(); j++) {
399 const DiStat &ds = ps[j];
400 if (ds.sdeviation < devThreshold) {
401 qreal f = ds.prop->getNormalized(c1, c2);
402 qreal expPart = (ds.average - f) / (ds.sdeviation + 0.1f);
403 qreal dinuclWeight = exp((-1) * expPart * expPart) / (ds.sdeviation + 0.1f);
404 posWeights[j] += dinuclWeight;
405 }
406 }
407 }
408 }
409 // normalize psums by nSamples
410 for (int i = 0; i < modelSize; i++) {
411 QVector<qreal> &posWeights = aveWeightMatrix[i];
412 for (int j = 0; j < posWeights.size(); j++) {
413 posWeights[j] /= nSamples;
414 }
415 }
416 #ifdef _SITECON_ALG_DEBUG_
417 dumpWeights("mave.txt", aveWeightMatrix, settings);
418 #endif
419 if (si.cancelFlag) {
420 return 0;
421 }
422
423 // Part2
424 // 2. compute max weights per props for model
425 for (int i = 0; i < modelSize && !si.cancelFlag; i++) {
426 PositionStats &ps = normMatrix[i];
427 QVector<qreal> &posWeights = maxWeightMatrix[i];
428 QVector<qreal> &aveWeights = aveWeightMatrix[i];
429 for (int j = 0; j < ps.size(); j++) {
430 const DiStat &ds = ps[j];
431 qreal maxProp = 100;
432 if (ds.sdeviation < devThreshold) {
433 for (int k = 0; k < ma->getNumRows(); k++) {
434 char c1 = ma->charAt(k, i);
435 char c2 = ma->charAt(k, i + 1);
436
437 if (c1 == 'N' || c2 == 'N') {
438 continue;
439 }
440
441 qreal f = ds.prop->getNormalized(c1, c2);
442 qreal expPart = (ds.average - f) / (ds.sdeviation + 0.1f);
443 qreal dinuclWeight = exp((-1) * expPart * expPart) / (ds.sdeviation + 0.1f);
444 maxProp = qMin(maxProp, dinuclWeight); // qMin is used instead of qMax for compatibility with original Sitecon
445 }
446 } else {
447 maxProp = aveWeights[j]; // actually any value is OK -> it will be filtered from weight estimation algorithm
448 }
449 posWeights[j] = maxProp;
450 }
451 }
452
453 #ifdef _SITECON_ALG_DEBUG_
454 dumpWeights("mmax.txt", maxWeightMatrix, settings);
455 #endif
456
457 // select MAX_WEIGHTS properties
458 for (int i = 0; i < modelSize; i++) {
459 QVector<qreal> &avePosWeights = aveWeightMatrix[i];
460 QVector<qreal> &maxPosWeights = maxWeightMatrix[i];
461 assert(avePosWeights.size() == maxPosWeights.size());
462 QVector<qreal> diffs;
463 for (int j = 0; j < maxPosWeights.size(); j++) {
464 qreal avePropWeight = avePosWeights[j];
465 qreal maxPropWeight = maxPosWeights[j];
466 qreal diff = maxPropWeight - avePropWeight;
467 diffs.append(diff);
468 }
469
470 PositionStats &psNorm = normMatrix[i];
471 PositionStats &psOrig = origMatrix[i];
472 for (int x = 0; x < MAX_WEIGHTS_ALG2; x++) {
473 qreal maxVal = -110;
474 for (int j = 0; j < psNorm.size(); j++) {
475 qreal val = diffs[j];
476 const DiStat &ds = psNorm[j];
477 if (val > maxVal && ds.sdeviation < devThreshold) {
478 maxVal = val;
479 }
480 }
481 // int nWeightedPerStep = 0;
482 for (int j = 0; j < psNorm.size(); j++) {
483 qreal val = diffs[j];
484 const DiStat &ds = psNorm[j];
485 if (val == maxVal && ds.sdeviation < devThreshold) {
486 diffs[j] = -100;
487 psNorm[j].weighted = true;
488 psOrig[j].weighted = true;
489 // nWeightedPerStep++;
490 }
491 }
492 // assert(nWeightedPerStep <= 1);
493 }
494 }
495
496 #ifdef _DEBUG
497 /*for(int i=0; i < modelSize; i++) {
498 int nWeights = 0;
499 const PositionStats& ps = origMatrix[i];
500 for (int j = 0; j < ps.size(); j++) {
501 const DiStat& ds = ps[j];
502 nWeights+=ds.weighted ? 1 : 0;
503 }
504 assert(nWeights <= MAX_WEIGHTS_ALG2);
505 }*/
506 #endif
507
508 return MAX_WEIGHTS_ALG2;
509 }
510
checkState(bool doAssert) const511 bool SiteconModel::checkState(bool doAssert) const {
512 // 1 check settings
513 assert(!doAssert || settings.windowSize > 0);
514 Q_UNUSED(doAssert);
515 if (settings.windowSize <= 0) {
516 return false;
517 }
518 assert(!doAssert || settings.secondTypeErrorCalibrationLen > settings.windowSize);
519 if (settings.secondTypeErrorCalibrationLen <= settings.windowSize) {
520 return false;
521 }
522 assert(!doAssert || (settings.chisquare > 0 && settings.chisquare < 1));
523 if (settings.chisquare <= 0 || settings.chisquare >= 1) {
524 return false;
525 }
526 assert(!doAssert || settings.numSequencesInAlignment > 1);
527 if (settings.numSequencesInAlignment <= 1) {
528 return false;
529 }
530
531 // 2 check model
532 assert(!doAssert || matrix.size() == settings.windowSize - 1);
533 if (matrix.size() != settings.windowSize - 1) {
534 return false;
535 }
536 for (int pos = 0; pos < matrix.size(); pos++) {
537 const PositionStats &posStat = matrix[pos];
538 assert(!doAssert || posStat.size() == settings.props.size());
539 int nWeights = 0;
540 for (int i = 0; i < posStat.size(); i++) {
541 const DiStat &ds = posStat[i];
542 if (ds.weighted) {
543 nWeights++;
544 }
545 const DiPropertySitecon *p = settings.props[i];
546 assert(!doAssert || ds.prop == p);
547 if (ds.prop != p) {
548 return false;
549 }
550 }
551
552 if (settings.weightAlg == SiteconWeightAlg_None) {
553 assert(!doAssert || nWeights == settings.props.size());
554 if (nWeights != settings.props.size()) {
555 return false;
556 }
557 } else {
558 // assert(!doAssert || nWeights <= MAX_WEIGHTS_ALG2);
559 if (nWeights > MAX_WEIGHTS_ALG2) {
560 algoLog.trace(QString("Number of Algorithm 2 weights %1, pos %2, model name %3").arg(nWeights).arg(pos).arg(modelName));
561 // return false;
562 }
563 }
564 }
565 for (int i = 0; i < 100; i++) {
566 assert(!doAssert || (err1[i] >= 0 && err1[i] <= 1));
567 if (err1[i] < 0 && err1[i] > 1) {
568 return false;
569 }
570 assert(!doAssert || (err2[i] >= 0 && err2[i] <= 1));
571 if (err2[i] < 0 && err2[i] > 1) {
572 return false;
573 }
574 }
575 return true;
576 }
577
578 } // namespace U2
579