Zoltan2
Loading...
Searching...
No Matches
Zoltan2_MetricAnalyzer.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// Zoltan2: A package of combinatorial algorithms for scientific computing
4//
5// Copyright 2012 NTESS and the Zoltan2 contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10/* \file Zoltan2_MetricAnalyzer.hpp
11 * \brief Used by the Zoltan2 test driver for running \
12 simple pass fail tests based on ranges of problem metrics.
13 */
14#ifndef ZOLTAN2_METRIC_ANALYZER_HPP
15#define ZOLTAN2_METRIC_ANALYZER_HPP
16
18#include <Zoltan2_Typedefs.hpp>
21#include <Teuchos_DefaultComm.hpp>
22#include <Teuchos_XMLObject.hpp>
23#include <Teuchos_FileInputSource.hpp>
24
25#include <sstream>
26#include <string>
27#include <iostream>
28
29using Teuchos::ParameterList;
30using Teuchos::Comm;
31using Teuchos::RCP;
32using Teuchos::ArrayRCP;
33using namespace Zoltan2_TestingFramework;
34
44
45template<class Adapter>
47
48protected:
49 RCP<Zoltan2::EvaluateBaseClass<Adapter>> evaluate_;
50
51public:
52 #define KEYWORD_PARAMETER_NAME "check" // should be the first entry
53 #define UPPER_PARAMETER_NAME "upper"
54 #define LOWER_PARAMETER_NAME "lower"
55
60 : evaluate_(evaluate) {
61 }
62
68 bool analyzeMetrics(const ParameterList &metricsParameters,
69 std::ostringstream & msg_stream )
70 {
71 if (metricsParameters.numParams() == 0) {
72 // specification is that we do nothing - we may just be testing our status
73 return true;
74 }
75
76 bool bAllPassed = true;
77
78 std::vector<MetricAnalyzerInfo> metricInfoSet;
79 LoadMetricInfo(metricInfoSet, metricsParameters);
80
81 int countFailedMetricChecks = 0;
82 for (auto metricInfo = metricInfoSet.begin();
83 metricInfo != metricInfoSet.end(); ++metricInfo) {
84 if (!MetricAnalyzer::executeMetricCheck(*metricInfo, msg_stream)) {
85 ++countFailedMetricChecks;
86 }
87 }
88
89 // this code prints a summary of all metric checks and indicates how many
90 // failed, if any did fail
91 if(countFailedMetricChecks == 0) {
92 msg_stream << metricsParameters.numParams() << " out of " <<
93 metricsParameters.numParams() << " metric checks" << " PASSED."
94 << std::endl;
95 }
96 else {
97 msg_stream << countFailedMetricChecks << " out of " <<
98 metricsParameters.numParams() << " metric checks " << " FAILED."
99 << std::endl;
100 bAllPassed = false;
101 }
102 msg_stream << std::endl; // cosmetic spacer
103 return bAllPassed;
104 }
105
111 const ParameterList & metricCheckParameters, std::string keyWord) const = 0;
112
113 void LoadMetricInfo(std::vector<MetricAnalyzerInfo> & metricInfoSet,
114 const ParameterList &metricsParameters) {
115
116 int headingIndex = 1;
117
118 for (auto iterateArbitraryHeadingNames = metricsParameters.begin();
119 iterateArbitraryHeadingNames != metricsParameters.end();
120 ++iterateArbitraryHeadingNames) {
121 auto headingName = metricsParameters.name(iterateArbitraryHeadingNames);
122
123 // we could be flexible on these headers but for now let's enforce it to
124 // get any convention inconsistencies cleaned up
125 std::string expectedHeadingName = "metriccheck" + std::to_string(headingIndex);
126 if( expectedHeadingName != headingName) {
127 throw std::logic_error(
128 "The parameter list expected to find a heading with name '"
129 + expectedHeadingName + "' but instead found '" + headingName );
130 }
131
132 // get the parameters specific to the check we want to run
134 metricsParameters.sublist(headingName));
135 metricInfoSet.push_back(metricInfo);
136 ++headingIndex;
137 }
138 }
139
145 const ParameterList & metricCheckParameters) const {
146
147 // first validate that all the string names in the metric check are correct
148 for (auto iterateAllKeys = metricCheckParameters.begin();
149 iterateAllKeys != metricCheckParameters.end(); ++iterateAllKeys) {
150 auto checkName = metricCheckParameters.name(iterateAllKeys);
151
152 bool bIsGeneralName = (checkName == KEYWORD_PARAMETER_NAME ||
153 checkName == UPPER_PARAMETER_NAME ||
154 checkName == LOWER_PARAMETER_NAME );
155
156 if (!bIsGeneralName && !isMetricCheckNameValid(checkName)) {
157 throw std::logic_error(
158 "Key name: '" + checkName + "' is not understood" );
159 }
160 }
161
162 if( !metricCheckParameters.isParameter(KEYWORD_PARAMETER_NAME)) {
163 throw std::logic_error( "Matric check must specify a key name using "
164 "the keyword " + std::string(KEYWORD_PARAMETER_NAME) );
165 }
166
167 std::string keyWord =
168 metricCheckParameters.get<std::string>(KEYWORD_PARAMETER_NAME);
169
170 // one of the parameters called "check" should define a string which is a
171 // keyword which correlates to an API call forthis class.
172 MetricAnalyzerInfo result
173 = getMetricResult(metricCheckParameters, keyWord);
174
175 // now we can obtain the upper and lower bounds for this test
176 result.bFoundUpperBound =
177 metricCheckParameters.isParameter(UPPER_PARAMETER_NAME);
178 result.bFoundLowerBound =
179 metricCheckParameters.isParameter(LOWER_PARAMETER_NAME);
180
181 if (result.bFoundUpperBound) {
182 result.upperValue =
183 metricCheckParameters.get<double>(UPPER_PARAMETER_NAME);
184 }
185 if (result.bFoundLowerBound) {
186 result.lowerValue =
187 metricCheckParameters.get<double>(LOWER_PARAMETER_NAME);
188 }
189
190 return result;
191 }
192
195 virtual bool isMetricCheckNameValid(std::string metricCheckName) const {
196 // EvaluatePartition will have special key words 'weight' and 'normed'
197 return false;
198 }
199
200private:
201 bool executeMetricCheck(const MetricAnalyzerInfo & metricInfo,
202 std::ostringstream &msg_stream)
203 {
204 bool bDoesThisTestPass = true; // will set this false if a test fails
205 if (metricInfo.bFoundUpperBound && metricInfo.bFoundLowerBound) {
206 if (metricInfo.theValue < metricInfo.lowerValue ||
207 metricInfo.theValue > metricInfo.upperValue) {
208 msg_stream << "FAILED: " << metricInfo.parameterDescription
209 << " value: " << metricInfo.theValue << " is not in range: "
210 << metricInfo.lowerValue << " to "
211 << metricInfo.upperValue << std::endl;
212 bDoesThisTestPass = false;
213 }
214 else {
215 msg_stream << "Success: " << metricInfo.parameterDescription
216 << " value: " << metricInfo.theValue << " is in range: "
217 << metricInfo.lowerValue << " to "
218 << metricInfo.upperValue << std::endl;
219 }
220 }
221 else if (metricInfo.bFoundUpperBound) {
222 if (metricInfo.theValue > metricInfo.upperValue) {
223 msg_stream << "FAILED: " << metricInfo.parameterDescription
224 << " value: " << metricInfo.theValue << " is not below "
225 << metricInfo.upperValue << std::endl;
226 bDoesThisTestPass = false;
227 }
228 else {
229 msg_stream << "Success: " << metricInfo.parameterDescription
230 << " value: " << metricInfo.theValue << " is below: "
231 << metricInfo.upperValue << std::endl;
232 }
233 }
234 else if (metricInfo.bFoundLowerBound) {
235 if (metricInfo.theValue < metricInfo.lowerValue) {
236 msg_stream << "FAILED: " << metricInfo.parameterDescription
237 << " value: " << metricInfo.theValue << " is not above "
238 << metricInfo.lowerValue << std::endl;
239 bDoesThisTestPass = false;
240 }
241 else {
242 msg_stream << "Success: " << metricInfo.parameterDescription
243 << " value: " << metricInfo.theValue << " is above: "
244 << metricInfo.lowerValue << std::endl;
245 }
246 }
247 return bDoesThisTestPass;
248 }
249};
250
251template<class Adapter>
253public:
254 // defines for metric checks - these are key words used in xml
255 #define WEIGHT_PARAMETER_NAME "weight"
256 #define NORMED_PARAMETER_NAME "normed"
257
262 : MetricAnalyzer<Adapter>(evaluate) {
263 }
264
269 const ParameterList & metricCheckParameters, std::string keyWord) const {
270
271 RCP<Zoltan2::EvaluatePartition<Adapter>> pEvaluatePartition =
272 Teuchos::rcp_dynamic_cast<Zoltan2::EvaluatePartition<Adapter>>(this->evaluate_);
273
274 MetricAnalyzerInfo result;
275
276 // didn't want to duplicate this value - a weight index should be 0 or
277 // larger but it's optional to specify it
278 #define UNDEFINED_PARAMETER_INT_INDEX -1
279
280 // Read the weight index parameter and throw if not a good format
281 // This is an optional parameter for EvaluatePartition
282 int weightIndex = UNDEFINED_PARAMETER_INT_INDEX;
283 if( metricCheckParameters.isParameter(WEIGHT_PARAMETER_NAME)) {
284 weightIndex = metricCheckParameters.get<int>(WEIGHT_PARAMETER_NAME);
285 if( weightIndex < 0 ) {
286 throw std::logic_error( "Optional weight index was specified as: " +
287 std::to_string(weightIndex) +
288 " Weight index must be 0 or positive." );
289 }
290 }
291
292 // Read the norm index and throw if not a good format
293 // This is an optional parameter for EvaluatePartition
294 int normedSetting = UNDEFINED_PARAMETER_INT_INDEX;
295 if( metricCheckParameters.isParameter(NORMED_PARAMETER_NAME)) {
296 bool bNormSetting = metricCheckParameters.get<bool>(NORMED_PARAMETER_NAME);
297 normedSetting = bNormSetting ? 1 : 0;
298 if( normedSetting != 0 && normedSetting != 1 ) {
299 throw std::logic_error( "Optional normed parameter was specified as: "
300 + std::to_string(normedSetting) +
301 " Normed parameter must be true or false." );
302 }
303 }
304
305 if( weightIndex != UNDEFINED_PARAMETER_INT_INDEX &&
306 normedSetting != UNDEFINED_PARAMETER_INT_INDEX ) {
307 throw std::logic_error( "Both parameters 'normed' and 'weight' were "
308 " specified. They should never appear together." );
309 }
310
311 // these define key names which convert to an API call
312 #define API_STRING_getWeightImbalance "imbalance"
313 #define API_STRING_getTotalEdgeCuts "total edge cuts"
314 #define API_STRING_getMaxEdgeCuts "max edge cuts"
315
316 // throw if normed set and weight is set
317 if( keyWord != API_STRING_getWeightImbalance &&
318 normedSetting != UNDEFINED_PARAMETER_INT_INDEX ) {
319 throw std::logic_error( "'normed' was specified but this only has meaning"
320 " for the 'imbalance' parameter." );
321 }
322
323 // Enforcing parallel usage to the API calls exist in EvaluatePartition
324 if (keyWord == API_STRING_getWeightImbalance) {
325 if( weightIndex == UNDEFINED_PARAMETER_INT_INDEX ) {
326 if( normedSetting == 1 ) {
327 result.theValue = pEvaluatePartition->getNormedImbalance();
328 }
329 else {
330 result.theValue = pEvaluatePartition->getObjectCountImbalance();
331 }
332 }
333 else {
334 // this will get the proper index specified
335 result.theValue = pEvaluatePartition->getWeightImbalance(weightIndex);
336 }
337 }
338 else if (keyWord == API_STRING_getTotalEdgeCuts) {
339 if( weightIndex == UNDEFINED_PARAMETER_INT_INDEX ) {
340 result.theValue = pEvaluatePartition->getTotalEdgeCut();
341 }
342 else {
343 result.theValue = pEvaluatePartition->getTotalWeightEdgeCut(weightIndex);
344 }
345 }
346 else if (keyWord == API_STRING_getMaxEdgeCuts) {
347 if( weightIndex == UNDEFINED_PARAMETER_INT_INDEX ) {
348 result.theValue = pEvaluatePartition->getMaxEdgeCut();
349 }
350 else {
351 result.theValue = pEvaluatePartition->getMaxWeightEdgeCut(weightIndex);
352 }
353 }
354 else {
355 // we have found an invalid key word - throw an error
356 throw std::logic_error( "The parameter '" +
357 std::string(KEYWORD_PARAMETER_NAME) + "' was specified as '" +
358 keyWord + "' which is not understood." );
359 }
360
361 result.parameterDescription = keyWord;
362 if( weightIndex != UNDEFINED_PARAMETER_INT_INDEX ) {
363 result.parameterDescription = result.parameterDescription +
364 " (weight: " + std::to_string(weightIndex) + ")";
365 }
366 else if( normedSetting != UNDEFINED_PARAMETER_INT_INDEX ) {
367 // throw above would catch the case where both of these were set
368 result.parameterDescription = result.parameterDescription + " (normed: "
369 + ( ( normedSetting == 0 ) ? "false" : "true" ) + ")";
370 }
371
372 return result;
373 }
374
377 virtual bool isMetricCheckNameValid(std::string metricCheckName) const {
378 return (metricCheckName == WEIGHT_PARAMETER_NAME ||
379 metricCheckName == NORMED_PARAMETER_NAME);
380 }
381};
382
383template<class Adapter>
385public:
386
387 // these define key names which convert to an API call
388 #define API_STRING_getBandwidth "bandwidth"
389 #define API_STRING_getEnvelope "envelope"
390 #define API_STRING_getSeparatorSize "separator size"
391
396 : MetricAnalyzer<Adapter>(evaluate) {
397 }
398
403 const ParameterList & metricCheckParameters, std::string keyWord) const {
404
405 RCP<Zoltan2::EvaluateOrdering<Adapter>> pEvaluateOrdering =
406 Teuchos::rcp_dynamic_cast<Zoltan2::EvaluateOrdering<Adapter>>(this->evaluate_);
407
408 MetricAnalyzerInfo result;
409
410 if (keyWord == API_STRING_getBandwidth) {
411 result.theValue = pEvaluateOrdering->getBandwidth();
412 }
413 else if (keyWord == API_STRING_getEnvelope) {
414 result.theValue = pEvaluateOrdering->getEnvelope();
415 }
416 else if (keyWord == API_STRING_getSeparatorSize) {
417 result.theValue = pEvaluateOrdering->getSeparatorSize();
418 }
419 else {
420 // we have found an invalid key word - throw an error
421 throw std::logic_error( "The parameter '" +
422 std::string(KEYWORD_PARAMETER_NAME) + "' was specified as '" +
423 keyWord + "' which is not understood." );
424 }
425
426 result.parameterDescription = keyWord; // just give default name for now
427
428 return result;
429 }
430};
431
432#endif //ZOLTAN2_METRIC_ANALYZER_HPP
Defines the Zoltan2_EvaluateOrdering.hpp class.
Defines the EvaluatePartition class.
#define KEYWORD_PARAMETER_NAME
#define UNDEFINED_PARAMETER_INT_INDEX
#define WEIGHT_PARAMETER_NAME
#define API_STRING_getEnvelope
#define API_STRING_getTotalEdgeCuts
#define API_STRING_getBandwidth
#define LOWER_PARAMETER_NAME
#define UPPER_PARAMETER_NAME
#define API_STRING_getWeightImbalance
#define NORMED_PARAMETER_NAME
#define API_STRING_getSeparatorSize
#define API_STRING_getMaxEdgeCuts
common code used by tests
keep typedefs that commonly appear in many places localized
virtual MetricAnalyzerInfo getMetricResult(const ParameterList &metricCheckParameters, std::string keyWord) const
Reads a metric value for bounds checking. Handle any special optional parameters.
MetricAnalyzerEvaluateOrdering(RCP< Zoltan2::EvaluateBaseClass< Adapter > > evaluate)
MetricAnalyzerEvaluatePartition constructor.
virtual MetricAnalyzerInfo getMetricResult(const ParameterList &metricCheckParameters, std::string keyWord) const
Reads a metric value for bounds checking. Handle any special optional parameters.
virtual bool isMetricCheckNameValid(std::string metricCheckName) const
Return true for any names we accept.
MetricAnalyzerEvaluatePartition(RCP< Zoltan2::EvaluateBaseClass< Adapter > > evaluate)
MetricAnalyzerEvaluatePartition constructor.
RCP< Zoltan2::EvaluateBaseClass< Adapter > > evaluate_
bool analyzeMetrics(const ParameterList &metricsParameters, std::ostringstream &msg_stream)
analyzeMetrics for a problem based on a range of tolerances
MetricAnalyzerInfo getMetricAnalyzerInfo(const ParameterList &metricCheckParameters) const
getMetricAnalyzerInfo is responsible for reading a metric value and then checking it against upper an...
MetricAnalyzer(RCP< Zoltan2::EvaluateBaseClass< Adapter > > evaluate)
MetricAnalyzer constructor takes an EvaluateBaseClass such as EvaluateOrdering or EvaluatePartition.
virtual bool isMetricCheckNameValid(std::string metricCheckName) const
Return true for any names we accept.
void LoadMetricInfo(std::vector< MetricAnalyzerInfo > &metricInfoSet, const ParameterList &metricsParameters)
virtual MetricAnalyzerInfo getMetricResult(const ParameterList &metricCheckParameters, std::string keyWord) const =0
getMetricValue is abstract and the derived class must define the proper method to check optional valu...