diff --git a/INIT/INITStepDesc.m b/INIT/INITStepDesc.m index 91ec541c..22e8a67d 100644 --- a/INIT/INITStepDesc.m +++ b/INIT/INITStepDesc.m @@ -24,7 +24,9 @@ % .mets Names of metabolites to remove % .compsToKeep Compartments for which metabolites should be kept. MILPParams %Cell array of MILPparams - dictates how many iterations that will be run in this step. - %Typically, MIPGap and TimeLimit is specified + %Typically, MIPGap and TimeLimit is specified. ftINIT additionally + %defaults Threads to 1 (single-threaded Gurobi) for determinism; + %set Threads here to override (0 = use all cores). AbsMIPGaps %If the objective is close to zero, a percentage of that is very small. %Therefore, also set an absolut value for this (typically 10 or 20). %For practical reasons, the first number is not used diff --git a/INIT/ftINIT.m b/INIT/ftINIT.m index 29654b29..ab5f3e03 100644 --- a/INIT/ftINIT.m +++ b/INIT/ftINIT.m @@ -233,6 +233,13 @@ if ~isfield(params, 'TimeLimit') params.TimeLimit = 5000; end + + %Default to single-threaded Gurobi MILP solving. Multi-threaded Gurobi + %can non-deterministically report the MILP as infeasible (issue #607). + %Override by setting 'Threads' in the step's MILPParams (0 = all cores). + if ~isfield(params, 'Threads') + params.Threads = 1; + end if ~first %There is sometimes a problem with that the objective function becomes close to zero, diff --git a/INIT/ftINITFillGapsMILP.m b/INIT/ftINITFillGapsMILP.m index cc66f33c..3eb51ac7 100644 --- a/INIT/ftINITFillGapsMILP.m +++ b/INIT/ftINITFillGapsMILP.m @@ -207,6 +207,7 @@ params.TimeLimit = 300; params.Seed = 26;%This is weird - although it says "optimal solution found", we can get different results with different %values of the objective function, where one is more optimal than the other (pretty big difference...) +params.Threads = 1; %single-threaded Gurobi MILP for determinism, see issue #607 %params.CSClientLog = 3;%generates a warning in gurobi, but may be of interest for other solvers % Optimize the problem diff --git a/INIT/removeLowScoreGenes.m b/INIT/removeLowScoreGenes.m index 76a5bdeb..a98a65cf 100644 --- a/INIT/removeLowScoreGenes.m +++ b/INIT/removeLowScoreGenes.m @@ -109,13 +109,20 @@ % regenerate "genes" and "rxnGeneMat" model fields [genes,rxnGeneMat] = getGenesFromGrRules(newModel.grRules); -newModel.genes = genes; -newModel.rxnGeneMat = rxnGeneMat; -% update other gene-related fields -remInd = ~ismember(model.genes,newModel.genes); +% determine which of the original genes were removed +remInd = ~ismember(model.genes,genes); remGenes = model.genes(remInd); +% Keep the retained genes in their original order rather than the sorted +% order returned by getGenesFromGrRules. Gene removal never introduces new +% genes, so the remaining genes are model.genes(~remInd). Preserving this +% order ensures the gene-associated fields trimmed below (geneShortNames, +% proteins, etc.), which are indexed by remInd, stay aligned with genes. +newModel.genes = model.genes(~remInd); +[~,reorderInd] = ismember(newModel.genes,genes); +newModel.rxnGeneMat = rxnGeneMat(:,reorderInd); + if isfield(newModel,'geneShortNames') newModel.geneShortNames(remInd) = []; end diff --git a/doc/INIT/INITStepDesc.html b/doc/INIT/INITStepDesc.html index 02850578..113533f7 100644 --- a/doc/INIT/INITStepDesc.html +++ b/doc/INIT/INITStepDesc.html @@ -70,55 +70,57 @@