Skip to content

Commit 174c99f

Browse files
author
Anton Eriksson
committed
fix: Constrain numerical arguments to sensible values
1 parent be3effd commit 174c99f

File tree

3 files changed

+103
-33
lines changed

3 files changed

+103
-33
lines changed

src/io/Config.cpp

+21-27
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ Config::Config(const std::string& flags, bool isCLI) : isCLI(isCLI)
3131

3232
api.setGroups({ "Input", "Algorithm", "Accuracy", "Output" });
3333

34-
bool deprecated_includeSelfLinks = false;
3534
std::vector<std::string> optionalOutputDir; // Used if !isCLI
3635
// --------------------- Input options ---------------------
3736
if (isCLI) {
@@ -44,23 +43,24 @@ Config::Config(const std::string& flags, bool isCLI) : isCLI(isCLI)
4443

4544
api.addOptionArgument(bipartiteTeleportation, "bipartite-teleportation", "Teleport like the bipartite flow instead of two-step (unipartite) teleportation.", "Input", true);
4645

47-
api.addOptionArgument(weightThreshold, "weight-threshold", "Limit the number of links to read from the network. Ignore links with less weight than the threshold.", ArgType::number, "Input", true);
46+
api.addOptionArgument(weightThreshold, "weight-threshold", "Limit the number of links to read from the network. Ignore links with less weight than the threshold.", ArgType::number, "Input", 0.0, true);
4847

48+
bool deprecated_includeSelfLinks = false;
4949
api.addOptionArgument(deprecated_includeSelfLinks, 'k', "include-self-links", "DEPRECATED. Include self links by default now, exclude with --no-self-links.", "Input", true).setHidden(true);
5050

5151
api.addOptionArgument(noSelfLinks, "no-self-links", "Exclude self links in the input network.", "Input", true);
5252

53-
api.addOptionArgument(nodeLimit, "node-limit", "Limit the number of nodes to read from the network. Ignore links connected to ignored nodes.", ArgType::integer, "Input", true);
53+
api.addOptionArgument(nodeLimit, "node-limit", "Limit the number of nodes to read from the network. Ignore links connected to ignored nodes.", ArgType::integer, "Input", 1u, true);
5454

55-
api.addOptionArgument(matchableMultilayerIds, "matchable-multilayer-ids", "Construct state ids from node and layer ids that are consistent across networks for the same max number of layers. Set to at least the largest layer id among networks to match.", ArgType::integer, "Input", true);
55+
api.addOptionArgument(matchableMultilayerIds, "matchable-multilayer-ids", "Construct state ids from node and layer ids that are consistent across networks for the same max number of layers. Set to at least the largest layer id among networks to match.", ArgType::integer, "Input", 1u, true);
5656

5757
api.addOptionArgument(clusterDataFile, 'c', "cluster-data", "Provide an initial two-level (clu format) or multi-layer (tree format) solution.", ArgType::path, "Input");
5858

5959
api.addOptionArgument(assignToNeighbouringModule, "assign-to-neighbouring-module", "Assign nodes without module assignments (from --cluster-data) to the module assignment of a neighbouring node if possible.", "Input", true);
6060

6161
api.addOptionArgument(metaDataFile, "meta-data", "Provide meta data (clu format) that should be encoded.", ArgType::path, "Input", true);
6262

63-
api.addOptionArgument(metaDataRate, "meta-data-rate", "Metadata encoding rate. Default is to encode each step.", ArgType::number, "Input", true);
63+
api.addOptionArgument(metaDataRate, "meta-data-rate", "Metadata encoding rate. Default is to encode each step.", ArgType::number, "Input", 0.0, true);
6464

6565
api.addOptionArgument(unweightedMetaData, "meta-data-unweighted", "Don't weight meta data by node flow.", "Input", true);
6666

@@ -78,7 +78,7 @@ Config::Config(const std::string& flags, bool isCLI) : isCLI(isCLI)
7878

7979
api.addOptionArgument(printClu, "clu", "Write a clu file with the top cluster ids for each node.", "Output");
8080

81-
api.addOptionArgument(cluLevel, "clu-level", "For clu output, print modules at specified depth from root. Use -1 for bottom level modules.", ArgType::integer, "Output", true);
81+
api.addOptionArgument(cluLevel, "clu-level", "For clu output, print modules at specified depth from root. Use -1 for bottom level modules.", ArgType::integer, "Output", -1, true);
8282

8383
api.addOptionArgument(outputFormats, 'o', "output", "Comma-separated output formats without spaces, e.g. -o clu,tree,ftree. Options: clu, tree, ftree, newick, json, csv, network, states, flow.", ArgType::list, "Output", true);
8484

@@ -101,45 +101,44 @@ Config::Config(const std::string& flags, bool isCLI) : isCLI(isCLI)
101101

102102
api.addOptionArgument(teleportToNodes, "to-nodes", "Teleport to nodes instead of to links, assuming uniform node weights if no such input data.", "Algorithm", true);
103103

104-
api.addOptionArgument(teleportationProbability, 'p', "teleportation-probability", "Probability of teleporting to a random node or link.", ArgType::probability, "Algorithm", true);
104+
api.addOptionArgument(teleportationProbability, 'p', "teleportation-probability", "Probability of teleporting to a random node or link.", ArgType::probability, "Algorithm", 0.0, 1.0, true);
105105

106106
api.addOptionArgument(regularized, "regularized", "Effectively add a fully connected Bayesian prior network to not overfit due to missing links. Implies recorded teleportation", "Algorithm", true);
107107

108-
api.addOptionArgument(regularizationStrength, "regularization-strength", "Adjust relative strength of Bayesian prior network with this multiplier.", ArgType::number, "Algorithm", true);
108+
api.addOptionArgument(regularizationStrength, "regularization-strength", "Adjust relative strength of Bayesian prior network with this multiplier.", ArgType::number, "Algorithm", 0.0, true);
109109

110110
api.addOptionArgument(entropyBiasCorrection, "entropy-corrected", "Correct for negative entropy bias in small samples (many modules).", "Algorithm", true);
111111

112112
api.addOptionArgument(entropyBiasCorrectionMultiplier, "entropy-correction-strength", "Increase or decrease the default entropy correction with this factor.", ArgType::number, "Algorithm", true);
113113

114-
api.addOptionArgument(markovTime, "markov-time", "Scales link flow to change the cost of moving between modules. Higher values results in fewer modules.", ArgType::number, "Algorithm", true);
114+
api.addOptionArgument(markovTime, "markov-time", "Scales link flow to change the cost of moving between modules. Higher values results in fewer modules.", ArgType::number, "Algorithm", 0.0, true);
115115

116-
api.addOptionArgument(preferredNumberOfModules, "preferred-number-of-modules", "Penalize solutions the more they differ from this number.", ArgType::integer, "Algorithm", true);
116+
api.addOptionArgument(preferredNumberOfModules, "preferred-number-of-modules", "Penalize solutions the more they differ from this number.", ArgType::integer, "Algorithm", 1u, true);
117117

118-
api.addOptionArgument(multilayerRelaxRate, "multilayer-relax-rate", "Probability to relax the constraint to move only in the current layer.", ArgType::probability, "Algorithm", true);
118+
api.addOptionArgument(multilayerRelaxRate, "multilayer-relax-rate", "Probability to relax the constraint to move only in the current layer.", ArgType::probability, "Algorithm", 0.0, 1.0, true);
119119

120-
api.addOptionArgument(multilayerRelaxLimit, "multilayer-relax-limit", "Number of neighboring layers in each direction to relax to. If negative, relax to any layer.", ArgType::integer, "Algorithm", true);
120+
api.addOptionArgument(multilayerRelaxLimit, "multilayer-relax-limit", "Number of neighboring layers in each direction to relax to. If negative, relax to any layer.", ArgType::integer, "Algorithm", -1, true);
121121

122-
api.addOptionArgument(multilayerRelaxLimitUp, "multilayer-relax-limit-up", "Number of neighboring layers with higher id to relax to. If negative, relax to any layer.", ArgType::integer, "Algorithm", true);
122+
api.addOptionArgument(multilayerRelaxLimitUp, "multilayer-relax-limit-up", "Number of neighboring layers with higher id to relax to. If negative, relax to any layer.", ArgType::integer, "Algorithm", -1, true);
123123

124-
api.addOptionArgument(multilayerRelaxLimitDown, "multilayer-relax-limit-down", "Number of neighboring layers with lower id to relax to. If negative, relax to any layer.", ArgType::integer, "Algorithm", true);
124+
api.addOptionArgument(multilayerRelaxLimitDown, "multilayer-relax-limit-down", "Number of neighboring layers with lower id to relax to. If negative, relax to any layer.", ArgType::integer, "Algorithm", -1, true);
125125

126126
api.addOptionArgument(multilayerRelaxByJensenShannonDivergence, "multilayer-relax-by-jsd", "Relax proportional to the out-link similarity measured by the Jensen-Shannon divergence.", "Algorithm", true);
127127

128128
// --------------------- Performance and accuracy options ---------------------
129-
api.addOptionArgument(seedToRandomNumberGenerator, 's', "seed", "A seed (integer) to the random number generator for reproducible results.", ArgType::integer, "Accuracy");
129+
api.addOptionArgument(seedToRandomNumberGenerator, 's', "seed", "A seed (integer) to the random number generator for reproducible results.", ArgType::integer, "Accuracy", 1ul);
130130

131-
int _numTrials = 1;
132-
api.addOptionArgument(_numTrials, 'N', "num-trials", "Number of outer-most loops to run before picking the best solution.", ArgType::integer, "Accuracy");
131+
api.addOptionArgument(numTrials, 'N', "num-trials", "Number of outer-most loops to run before picking the best solution.", ArgType::integer, "Accuracy", 1u);
133132

134-
api.addOptionArgument(coreLoopLimit, 'M', "core-loop-limit", "Limit the number of loops that tries to move each node into the best possible module.", ArgType::integer, "Accuracy", true);
133+
api.addOptionArgument(coreLoopLimit, 'M', "core-loop-limit", "Limit the number of loops that tries to move each node into the best possible module.", ArgType::integer, "Accuracy", 1u, true);
135134

136-
api.addOptionArgument(levelAggregationLimit, 'L', "core-level-limit", "Limit the number of times the core loops are reapplied on existing modular network to search bigger structures.", ArgType::integer, "Accuracy", true);
135+
api.addOptionArgument(levelAggregationLimit, 'L', "core-level-limit", "Limit the number of times the core loops are reapplied on existing modular network to search bigger structures.", ArgType::integer, "Accuracy", 1u, true);
137136

138-
api.addOptionArgument(tuneIterationLimit, 'T', "tune-iteration-limit", "Limit the number of main iterations in the two-level partition algorithm. 0 means no limit.", ArgType::integer, "Accuracy", true);
137+
api.addOptionArgument(tuneIterationLimit, 'T', "tune-iteration-limit", "Limit the number of main iterations in the two-level partition algorithm. 0 means no limit.", ArgType::integer, "Accuracy", 1u, true);
139138

140-
api.addOptionArgument(minimumCodelengthImprovement, "core-loop-codelength-threshold", "Minimum codelength threshold for accepting a new solution in core loop.", ArgType::number, "Accuracy", true);
139+
api.addOptionArgument(minimumCodelengthImprovement, "core-loop-codelength-threshold", "Minimum codelength threshold for accepting a new solution in core loop.", ArgType::number, "Accuracy", 0.0, true);
141140

142-
api.addOptionArgument(minimumRelativeTuneIterationImprovement, "tune-iteration-relative-threshold", "Set codelength improvement threshold of each new tune iteration to 'f' times the initial two-level codelength.", ArgType::number, "Accuracy", true);
141+
api.addOptionArgument(minimumRelativeTuneIterationImprovement, "tune-iteration-relative-threshold", "Set codelength improvement threshold of each new tune iteration to 'f' times the initial two-level codelength.", ArgType::number, "Accuracy", 0.0, true);
143142

144143
api.addIncrementalOptionArgument(fastHierarchicalSolution, 'F', "fast-hierarchical-solution", "Find top modules fast. Use -FF to keep all fast levels. Use -FFF to skip recursive part.", "Accuracy", true);
145144

@@ -159,11 +158,6 @@ Config::Config(const std::string& flags, bool isCLI) : isCLI(isCLI)
159158
throw std::runtime_error("The --include-self-links flag is deprecated to include self links by default. Use --no-self-links to exclude.");
160159
}
161160

162-
if (_numTrials < 1) {
163-
throw std::runtime_error("Must use at least 1 trial.");
164-
}
165-
numTrials = static_cast<unsigned int>(_numTrials);
166-
167161
if (!optionalOutputDir.empty())
168162
outDirectory = optionalOutputDir[0];
169163

src/io/ProgramInterface.h

+60-6
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,36 @@ struct ArgumentOption : Option {
145145
T& target;
146146
};
147147

148+
template <typename T>
149+
struct LowerBoundArgumentOption : ArgumentOption<T> {
150+
LowerBoundArgumentOption(T& target, char shortName, std::string longName, std::string desc, std::string group, bool isAdvanced, std::string argName, T minValue)
151+
: ArgumentOption<T>(target, shortName, std::move(longName), std::move(desc), std::move(group), isAdvanced, std::move(argName)), minValue(minValue) { }
152+
153+
bool parse(std::string const& value) override
154+
{
155+
auto ok = ArgumentOption<T>::parse(value);
156+
if (ArgumentOption<T>::target < minValue) return false;
157+
return ok;
158+
}
159+
160+
T minValue;
161+
};
162+
163+
template <typename T>
164+
struct LowerUpperBoundArgumentOption : LowerBoundArgumentOption<T> {
165+
LowerUpperBoundArgumentOption(T& target, char shortName, std::string longName, std::string desc, std::string group, bool isAdvanced, std::string argName, T minValue, T maxValue)
166+
: LowerBoundArgumentOption<T>(target, shortName, std::move(longName), std::move(desc), std::move(group), isAdvanced, std::move(argName), minValue), maxValue(maxValue) { }
167+
168+
bool parse(std::string const& value) override
169+
{
170+
auto ok = LowerBoundArgumentOption<T>::parse(value);
171+
if (LowerBoundArgumentOption<T>::target > maxValue) return false;
172+
return ok;
173+
}
174+
175+
T maxValue;
176+
};
177+
148178
template <>
149179
struct ArgumentOption<bool> : Option {
150180
ArgumentOption(bool& target, char shortName, std::string longName, std::string desc, std::string group, bool isAdvanced)
@@ -302,9 +332,7 @@ class ProgramInterface {
302332
// Without shortName
303333
Option& addOptionArgument(bool& target, std::string longName, std::string description, std::string group, bool isAdvanced = false)
304334
{
305-
auto* o = new ArgumentOption<bool>(target, '\0', std::move(longName), std::move(description), std::move(group), isAdvanced);
306-
m_optionArguments.emplace_back(o);
307-
return *o;
335+
return addOptionArgument(target, '\0', std::move(longName), std::move(description), std::move(group), isAdvanced);
308336
}
309337

310338
template <typename T>
@@ -315,15 +343,41 @@ class ProgramInterface {
315343
return *o;
316344
}
317345

318-
// Without shortName
319346
template <typename T>
320-
Option& addOptionArgument(T& target, std::string longName, std::string description, std::string argumentName, std::string group, bool isAdvanced = false)
347+
Option& addOptionArgument(T& target, char shortName, std::string longName, std::string description, std::string argumentName, std::string group, T minValue, bool isAdvanced = false)
321348
{
322-
auto* o = new ArgumentOption<T>(target, '\0', std::move(longName), std::move(description), std::move(group), isAdvanced, std::move(argumentName));
349+
auto* o = new LowerBoundArgumentOption<T>(target, shortName, std::move(longName), std::move(description), std::move(group), isAdvanced, std::move(argumentName), minValue);
323350
m_optionArguments.emplace_back(o);
324351
return *o;
325352
}
326353

354+
template <typename T>
355+
Option& addOptionArgument(T& target, char shortName, std::string longName, std::string description, std::string argumentName, std::string group, T minValue, T maxValue, bool isAdvanced = false)
356+
{
357+
auto* o = new LowerUpperBoundArgumentOption<T>(target, shortName, std::move(longName), std::move(description), std::move(group), isAdvanced, std::move(argumentName), minValue, maxValue);
358+
m_optionArguments.emplace_back(o);
359+
return *o;
360+
}
361+
362+
// Without shortName
363+
template <typename T>
364+
Option& addOptionArgument(T& target, std::string longName, std::string description, std::string argumentName, std::string group, bool isAdvanced = false)
365+
{
366+
return addOptionArgument(target, '\0', std::move(longName), std::move(description), std::move(argumentName), std::move(group), isAdvanced);
367+
}
368+
369+
template <typename T>
370+
Option& addOptionArgument(T& target, std::string longName, std::string description, std::string argumentName, std::string group, T minValue, bool isAdvanced = false)
371+
{
372+
return addOptionArgument(target, '\0', std::move(longName), std::move(description), std::move(argumentName), std::move(group), minValue, isAdvanced);
373+
}
374+
375+
template <typename T>
376+
Option& addOptionArgument(T& target, std::string longName, std::string description, std::string argumentName, std::string group, T minValue, T maxValue, bool isAdvanced = false)
377+
{
378+
return addOptionArgument(target, '\0', std::move(longName), std::move(description), std::move(argumentName), std::move(group), minValue, maxValue, isAdvanced);
379+
}
380+
327381
void parseArgs(const std::string& args);
328382

329383
std::vector<ParsedOption> getUsedOptionArguments() const;

src/utils/convert.h

+22
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,28 @@ namespace io {
151151
return !!(istream >> value);
152152
}
153153

154+
template <>
155+
inline bool stringToValue(std::string const& str, unsigned int& value)
156+
{
157+
std::istringstream istream(str);
158+
int target = 0;
159+
istream >> target;
160+
if (target < 0) return false;
161+
value = target;
162+
return true;
163+
}
164+
165+
template <>
166+
inline bool stringToValue(std::string const& str, unsigned long& value)
167+
{
168+
std::istringstream istream(str);
169+
int target = 0;
170+
istream >> target;
171+
if (target < 0) return false;
172+
value = target;
173+
return true;
174+
}
175+
154176
inline std::string firstWord(const std::string& line)
155177
{
156178
std::istringstream ss;

0 commit comments

Comments
 (0)