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