Zoltan2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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 
17 #include <Zoltan2_TestHelpers.hpp>
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 
29 using Teuchos::ParameterList;
30 using Teuchos::Comm;
31 using Teuchos::RCP;
32 using Teuchos::ArrayRCP;
33 using namespace Zoltan2_TestingFramework;
34 
36 {
37  double theValue;
38  double upperValue;
39  double lowerValue;
42  std::string parameterDescription;
43 };
44 
45 template<class Adapter>
47 
48 protected:
49  RCP<Zoltan2::EvaluateBaseClass<Adapter>> evaluate_;
50 
51 public:
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 
110  virtual MetricAnalyzerInfo getMetricResult(
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
133  MetricAnalyzerInfo metricInfo = getMetricAnalyzerInfo(
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 
200 private:
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 
251 template<class Adapter>
253 public:
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 
383 template<class Adapter>
385 public:
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
keep typedefs that commonly appear in many places localized
#define API_STRING_getMaxEdgeCuts
virtual bool isMetricCheckNameValid(std::string metricCheckName) const
Return true for any names we accept.
MetricAnalyzerEvaluateOrdering(RCP< Zoltan2::EvaluateBaseClass< Adapter >> evaluate)
MetricAnalyzerEvaluatePartition constructor.
MetricAnalyzerInfo getMetricAnalyzerInfo(const ParameterList &metricCheckParameters) const
getMetricAnalyzerInfo is responsible for reading a metric value and then checking it against upper an...
#define UPPER_PARAMETER_NAME
common code used by tests
virtual bool isMetricCheckNameValid(std::string metricCheckName) const
Return true for any names we accept.
A class that computes and returns quality metrics. base class for the local and global ordering vers...
void LoadMetricInfo(std::vector< MetricAnalyzerInfo > &metricInfoSet, const ParameterList &metricsParameters)
#define API_STRING_getTotalEdgeCuts
#define API_STRING_getEnvelope
#define LOWER_PARAMETER_NAME
Defines the EvaluatePartition class.
#define UNDEFINED_PARAMETER_INT_INDEX
#define NORMED_PARAMETER_NAME
#define API_STRING_getWeightImbalance
Defines the Zoltan2_EvaluateOrdering.hpp class.
bool analyzeMetrics(const ParameterList &metricsParameters, std::ostringstream &msg_stream)
analyzeMetrics for a problem based on a range of tolerances
#define KEYWORD_PARAMETER_NAME
virtual MetricAnalyzerInfo getMetricResult(const ParameterList &metricCheckParameters, std::string keyWord) const
Reads a metric value for bounds checking. Handle any special optional parameters. ...
#define API_STRING_getSeparatorSize
#define API_STRING_getBandwidth
MetricAnalyzer(RCP< Zoltan2::EvaluateBaseClass< Adapter >> evaluate)
MetricAnalyzer constructor takes an EvaluateBaseClass such as EvaluateOrdering or EvaluatePartition...
A class that computes and returns quality metrics.
MetricAnalyzerEvaluatePartition(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. ...
#define WEIGHT_PARAMETER_NAME
RCP< Zoltan2::EvaluateBaseClass< Adapter > > evaluate_