Zoltan2
Loading...
Searching...
No Matches
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
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
65using Teuchos::ParameterList;
66using Teuchos::Comm;
67using Teuchos::RCP;
68using Teuchos::ArrayRCP;
69using namespace Zoltan2_TestingFramework;
70
80
81template<class Adapter>
83
84protected:
85 RCP<Zoltan2::EvaluateBaseClass<Adapter>> evaluate_;
86
87public:
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
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
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
236private:
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
287template<class Adapter>
289public:
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
419template<class Adapter>
421public:
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
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...