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 // ***********************************************************************
4 //
5 // Zoltan2: A package of combinatorial algorithms for scientific computing
6 // Copyright 2012 Sandia Corporation
7 //
8 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9 // the U.S. Government retains certain rights in this software.
10 //
11 // Redistribution and use in source and binary forms, with or without
12 // modification, are permitted provided that the following conditions are
13 // met:
14 //
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the following disclaimer.
17 //
18 // 2. Redistributions in binary form must reproduce the above copyright
19 // notice, this list of conditions and the following disclaimer in the
20 // documentation and/or other materials provided with the distribution.
21 //
22 // 3. Neither the name of the Corporation nor the names of the
23 // contributors may be used to endorse or promote products derived from
24 // this software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 //
38 // Questions? Contact Karen Devine (kddevin@sandia.gov)
39 // Erik Boman (egboman@sandia.gov)
40 // Siva Rajamanickam (srajama@sandia.gov)
41 //
42 // ***********************************************************************
43 //
44 // @HEADER
45 
46 /* \file Zoltan2_MetricAnalyzer.hpp
47  * \brief Used by the Zoltan2 test driver for running \
48  simple pass fail tests based on ranges of problem metrics.
49  */
50 #ifndef ZOLTAN2_METRIC_ANALYZER_HPP
51 #define ZOLTAN2_METRIC_ANALYZER_HPP
52 
53 #include <Zoltan2_TestHelpers.hpp>
54 #include <Zoltan2_Typedefs.hpp>
57 #include <Teuchos_DefaultComm.hpp>
58 #include <Teuchos_XMLObject.hpp>
59 #include <Teuchos_FileInputSource.hpp>
60 
61 #include <sstream>
62 #include <string>
63 #include <iostream>
64 
65 using Teuchos::ParameterList;
66 using Teuchos::Comm;
67 using Teuchos::RCP;
68 using Teuchos::ArrayRCP;
69 using namespace Zoltan2_TestingFramework;
70 
72 {
73  double theValue;
74  double upperValue;
75  double lowerValue;
78  std::string parameterDescription;
79 };
80 
81 template<class Adapter>
83 
84 protected:
85  RCP<Zoltan2::EvaluateBaseClass<Adapter>> evaluate_;
86 
87 public:
88  #define KEYWORD_PARAMETER_NAME "check" // should be the first entry
89  #define UPPER_PARAMETER_NAME "upper"
90  #define LOWER_PARAMETER_NAME "lower"
91 
96  : evaluate_(evaluate) {
97  }
98 
104  bool analyzeMetrics(const ParameterList &metricsParameters,
105  std::ostringstream & msg_stream )
106  {
107  if (metricsParameters.numParams() == 0) {
108  // specification is that we do nothing - we may just be testing our status
109  return true;
110  }
111 
112  bool bAllPassed = true;
113 
114  std::vector<MetricAnalyzerInfo> metricInfoSet;
115  LoadMetricInfo(metricInfoSet, metricsParameters);
116 
117  int countFailedMetricChecks = 0;
118  for (auto metricInfo = metricInfoSet.begin();
119  metricInfo != metricInfoSet.end(); ++metricInfo) {
120  if (!MetricAnalyzer::executeMetricCheck(*metricInfo, msg_stream)) {
121  ++countFailedMetricChecks;
122  }
123  }
124 
125  // this code prints a summary of all metric checks and indicates how many
126  // failed, if any did fail
127  if(countFailedMetricChecks == 0) {
128  msg_stream << metricsParameters.numParams() << " out of " <<
129  metricsParameters.numParams() << " metric checks" << " PASSED."
130  << std::endl;
131  }
132  else {
133  msg_stream << countFailedMetricChecks << " out of " <<
134  metricsParameters.numParams() << " metric checks " << " FAILED."
135  << std::endl;
136  bAllPassed = false;
137  }
138  msg_stream << std::endl; // cosmetic spacer
139  return bAllPassed;
140  }
141 
146  virtual MetricAnalyzerInfo getMetricResult(
147  const ParameterList & metricCheckParameters, std::string keyWord) const = 0;
148 
149  void LoadMetricInfo(std::vector<MetricAnalyzerInfo> & metricInfoSet,
150  const ParameterList &metricsParameters) {
151 
152  int headingIndex = 1;
153 
154  for (auto iterateArbitraryHeadingNames = metricsParameters.begin();
155  iterateArbitraryHeadingNames != metricsParameters.end();
156  ++iterateArbitraryHeadingNames) {
157  auto headingName = metricsParameters.name(iterateArbitraryHeadingNames);
158 
159  // we could be flexible on these headers but for now let's enforce it to
160  // get any convention inconsistencies cleaned up
161  std::string expectedHeadingName = "metriccheck" + std::to_string(headingIndex);
162  if( expectedHeadingName != headingName) {
163  throw std::logic_error(
164  "The parameter list expected to find a heading with name '"
165  + expectedHeadingName + "' but instead found '" + headingName );
166  }
167 
168  // get the parameters specific to the check we want to run
169  MetricAnalyzerInfo metricInfo = getMetricAnalyzerInfo(
170  metricsParameters.sublist(headingName));
171  metricInfoSet.push_back(metricInfo);
172  ++headingIndex;
173  }
174  }
175 
181  const ParameterList & metricCheckParameters) const {
182 
183  // first validate that all the string names in the metric check are correct
184  for (auto iterateAllKeys = metricCheckParameters.begin();
185  iterateAllKeys != metricCheckParameters.end(); ++iterateAllKeys) {
186  auto checkName = metricCheckParameters.name(iterateAllKeys);
187 
188  bool bIsGeneralName = (checkName == KEYWORD_PARAMETER_NAME ||
189  checkName == UPPER_PARAMETER_NAME ||
190  checkName == LOWER_PARAMETER_NAME );
191 
192  if (!bIsGeneralName && !isMetricCheckNameValid(checkName)) {
193  throw std::logic_error(
194  "Key name: '" + checkName + "' is not understood" );
195  }
196  }
197 
198  if( !metricCheckParameters.isParameter(KEYWORD_PARAMETER_NAME)) {
199  throw std::logic_error( "Matric check must specify a key name using "
200  "the keyword " + std::string(KEYWORD_PARAMETER_NAME) );
201  }
202 
203  std::string keyWord =
204  metricCheckParameters.get<std::string>(KEYWORD_PARAMETER_NAME);
205 
206  // one of the parameters called "check" should define a string which is a
207  // keyword which correlates to an API call forthis class.
208  MetricAnalyzerInfo result
209  = getMetricResult(metricCheckParameters, keyWord);
210 
211  // now we can obtain the upper and lower bounds for this test
212  result.bFoundUpperBound =
213  metricCheckParameters.isParameter(UPPER_PARAMETER_NAME);
214  result.bFoundLowerBound =
215  metricCheckParameters.isParameter(LOWER_PARAMETER_NAME);
216 
217  if (result.bFoundUpperBound) {
218  result.upperValue =
219  metricCheckParameters.get<double>(UPPER_PARAMETER_NAME);
220  }
221  if (result.bFoundLowerBound) {
222  result.lowerValue =
223  metricCheckParameters.get<double>(LOWER_PARAMETER_NAME);
224  }
225 
226  return result;
227  }
228 
231  virtual bool isMetricCheckNameValid(std::string metricCheckName) const {
232  // EvaluatePartition will have special key words 'weight' and 'normed'
233  return false;
234  }
235 
236 private:
237  bool executeMetricCheck(const MetricAnalyzerInfo & metricInfo,
238  std::ostringstream &msg_stream)
239  {
240  bool bDoesThisTestPass = true; // will set this false if a test fails
241  if (metricInfo.bFoundUpperBound && metricInfo.bFoundLowerBound) {
242  if (metricInfo.theValue < metricInfo.lowerValue ||
243  metricInfo.theValue > metricInfo.upperValue) {
244  msg_stream << "FAILED: " << metricInfo.parameterDescription
245  << " value: " << metricInfo.theValue << " is not in range: "
246  << metricInfo.lowerValue << " to "
247  << metricInfo.upperValue << std::endl;
248  bDoesThisTestPass = false;
249  }
250  else {
251  msg_stream << "Success: " << metricInfo.parameterDescription
252  << " value: " << metricInfo.theValue << " is in range: "
253  << metricInfo.lowerValue << " to "
254  << metricInfo.upperValue << std::endl;
255  }
256  }
257  else if (metricInfo.bFoundUpperBound) {
258  if (metricInfo.theValue > metricInfo.upperValue) {
259  msg_stream << "FAILED: " << metricInfo.parameterDescription
260  << " value: " << metricInfo.theValue << " is not below "
261  << metricInfo.upperValue << std::endl;
262  bDoesThisTestPass = false;
263  }
264  else {
265  msg_stream << "Success: " << metricInfo.parameterDescription
266  << " value: " << metricInfo.theValue << " is below: "
267  << metricInfo.upperValue << std::endl;
268  }
269  }
270  else if (metricInfo.bFoundLowerBound) {
271  if (metricInfo.theValue < metricInfo.lowerValue) {
272  msg_stream << "FAILED: " << metricInfo.parameterDescription
273  << " value: " << metricInfo.theValue << " is not above "
274  << metricInfo.lowerValue << std::endl;
275  bDoesThisTestPass = false;
276  }
277  else {
278  msg_stream << "Success: " << metricInfo.parameterDescription
279  << " value: " << metricInfo.theValue << " is above: "
280  << metricInfo.lowerValue << std::endl;
281  }
282  }
283  return bDoesThisTestPass;
284  }
285 };
286 
287 template<class Adapter>
289 public:
290  // defines for metric checks - these are key words used in xml
291  #define WEIGHT_PARAMETER_NAME "weight"
292  #define NORMED_PARAMETER_NAME "normed"
293 
298  : MetricAnalyzer<Adapter>(evaluate) {
299  }
300 
305  const ParameterList & metricCheckParameters, std::string keyWord) const {
306 
307  RCP<Zoltan2::EvaluatePartition<Adapter>> pEvaluatePartition =
308  Teuchos::rcp_dynamic_cast<Zoltan2::EvaluatePartition<Adapter>>(this->evaluate_);
309 
310  MetricAnalyzerInfo result;
311 
312  // didn't want to duplicate this value - a weight index should be 0 or
313  // larger but it's optional to specify it
314  #define UNDEFINED_PARAMETER_INT_INDEX -1
315 
316  // Read the weight index parameter and throw if not a good format
317  // This is an optional parameter for EvaluatePartition
318  int weightIndex = UNDEFINED_PARAMETER_INT_INDEX;
319  if( metricCheckParameters.isParameter(WEIGHT_PARAMETER_NAME)) {
320  weightIndex = metricCheckParameters.get<int>(WEIGHT_PARAMETER_NAME);
321  if( weightIndex < 0 ) {
322  throw std::logic_error( "Optional weight index was specified as: " +
323  std::to_string(weightIndex) +
324  " Weight index must be 0 or positive." );
325  }
326  }
327 
328  // Read the norm index and throw if not a good format
329  // This is an optional parameter for EvaluatePartition
330  int normedSetting = UNDEFINED_PARAMETER_INT_INDEX;
331  if( metricCheckParameters.isParameter(NORMED_PARAMETER_NAME)) {
332  bool bNormSetting = metricCheckParameters.get<bool>(NORMED_PARAMETER_NAME);
333  normedSetting = bNormSetting ? 1 : 0;
334  if( normedSetting != 0 && normedSetting != 1 ) {
335  throw std::logic_error( "Optional normed parameter was specified as: "
336  + std::to_string(normedSetting) +
337  " Normed parameter must be true or false." );
338  }
339  }
340 
341  if( weightIndex != UNDEFINED_PARAMETER_INT_INDEX &&
342  normedSetting != UNDEFINED_PARAMETER_INT_INDEX ) {
343  throw std::logic_error( "Both parameters 'normed' and 'weight' were "
344  " specified. They should never appear together." );
345  }
346 
347  // these define key names which convert to an API call
348  #define API_STRING_getWeightImbalance "imbalance"
349  #define API_STRING_getTotalEdgeCuts "total edge cuts"
350  #define API_STRING_getMaxEdgeCuts "max edge cuts"
351 
352  // throw if normed set and weight is set
353  if( keyWord != API_STRING_getWeightImbalance &&
354  normedSetting != UNDEFINED_PARAMETER_INT_INDEX ) {
355  throw std::logic_error( "'normed' was specified but this only has meaning"
356  " for the 'imbalance' parameter." );
357  }
358 
359  // Enforcing parallel usage to the API calls exist in EvaluatePartition
360  if (keyWord == API_STRING_getWeightImbalance) {
361  if( weightIndex == UNDEFINED_PARAMETER_INT_INDEX ) {
362  if( normedSetting == 1 ) {
363  result.theValue = pEvaluatePartition->getNormedImbalance();
364  }
365  else {
366  result.theValue = pEvaluatePartition->getObjectCountImbalance();
367  }
368  }
369  else {
370  // this will get the proper index specified
371  result.theValue = pEvaluatePartition->getWeightImbalance(weightIndex);
372  }
373  }
374  else if (keyWord == API_STRING_getTotalEdgeCuts) {
375  if( weightIndex == UNDEFINED_PARAMETER_INT_INDEX ) {
376  result.theValue = pEvaluatePartition->getTotalEdgeCut();
377  }
378  else {
379  result.theValue = pEvaluatePartition->getTotalWeightEdgeCut(weightIndex);
380  }
381  }
382  else if (keyWord == API_STRING_getMaxEdgeCuts) {
383  if( weightIndex == UNDEFINED_PARAMETER_INT_INDEX ) {
384  result.theValue = pEvaluatePartition->getMaxEdgeCut();
385  }
386  else {
387  result.theValue = pEvaluatePartition->getMaxWeightEdgeCut(weightIndex);
388  }
389  }
390  else {
391  // we have found an invalid key word - throw an error
392  throw std::logic_error( "The parameter '" +
393  std::string(KEYWORD_PARAMETER_NAME) + "' was specified as '" +
394  keyWord + "' which is not understood." );
395  }
396 
397  result.parameterDescription = keyWord;
398  if( weightIndex != UNDEFINED_PARAMETER_INT_INDEX ) {
399  result.parameterDescription = result.parameterDescription +
400  " (weight: " + std::to_string(weightIndex) + ")";
401  }
402  else if( normedSetting != UNDEFINED_PARAMETER_INT_INDEX ) {
403  // throw above would catch the case where both of these were set
404  result.parameterDescription = result.parameterDescription + " (normed: "
405  + ( ( normedSetting == 0 ) ? "false" : "true" ) + ")";
406  }
407 
408  return result;
409  }
410 
413  virtual bool isMetricCheckNameValid(std::string metricCheckName) const {
414  return (metricCheckName == WEIGHT_PARAMETER_NAME ||
415  metricCheckName == NORMED_PARAMETER_NAME);
416  }
417 };
418 
419 template<class Adapter>
421 public:
422 
423  // these define key names which convert to an API call
424  #define API_STRING_getBandwidth "bandwidth"
425  #define API_STRING_getEnvelope "envelope"
426  #define API_STRING_getSeparatorSize "separator size"
427 
432  : MetricAnalyzer<Adapter>(evaluate) {
433  }
434 
439  const ParameterList & metricCheckParameters, std::string keyWord) const {
440 
441  RCP<Zoltan2::EvaluateOrdering<Adapter>> pEvaluateOrdering =
442  Teuchos::rcp_dynamic_cast<Zoltan2::EvaluateOrdering<Adapter>>(this->evaluate_);
443 
444  MetricAnalyzerInfo result;
445 
446  if (keyWord == API_STRING_getBandwidth) {
447  result.theValue = pEvaluateOrdering->getBandwidth();
448  }
449  else if (keyWord == API_STRING_getEnvelope) {
450  result.theValue = pEvaluateOrdering->getEnvelope();
451  }
452  else if (keyWord == API_STRING_getSeparatorSize) {
453  result.theValue = pEvaluateOrdering->getSeparatorSize();
454  }
455  else {
456  // we have found an invalid key word - throw an error
457  throw std::logic_error( "The parameter '" +
458  std::string(KEYWORD_PARAMETER_NAME) + "' was specified as '" +
459  keyWord + "' which is not understood." );
460  }
461 
462  result.parameterDescription = keyWord; // just give default name for now
463 
464  return result;
465  }
466 };
467 
468 #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_