Unverified Commit d62a9ded authored by Mark Hoemmen's avatar Mark Hoemmen Committed by GitHub
Browse files

Merge pull request #4834 from danielsjensen1/4793-modified-parameter-lists

Teuchos: Modified parameter lists - Issue 4793 - modified parameter lists
parents 564fb7da 4efe40e6
......@@ -40,7 +40,8 @@
// @HEADER
//#define TEUCHOS_PARAMETER_LIST_SHOW_TRACE
#include <deque>
#include <functional>
#include "Teuchos_ParameterList.hpp"
#include "Teuchos_FancyOStream.hpp"
#include "Teuchos_StrUtils.hpp"
......@@ -77,13 +78,9 @@ namespace Teuchos {
// Constructors/Destructor/Info
ParameterList::ParameterList()
:name_("ANONYMOUS"), disableRecursiveValidation_(false)
{}
ParameterList::ParameterList(const std::string &name_in)
:name_(name_in), disableRecursiveValidation_(false)
ParameterList::ParameterList(const std::string &name_in,
RCP<const ParameterListModifier> const& modifier_in)
:name_(name_in), modifier_(modifier_in)
{}
......@@ -92,6 +89,9 @@ ParameterList::ParameterList(const ParameterList& source)
name_ = source.name_;
params_ = source.params_;
disableRecursiveValidation_ = source.disableRecursiveValidation_;
disableRecursiveModification_= source.disableRecursiveModification_;
disableRecursiveReconciliation_ = source.disableRecursiveReconciliation_;
modifier_ = source.modifier_;
}
......@@ -112,10 +112,20 @@ ParameterList& ParameterList::operator=(const ParameterList& source)
name_ = source.name_;
params_ = source.params_;
disableRecursiveValidation_ = source.disableRecursiveValidation_;
disableRecursiveModification_= source.disableRecursiveModification_;
disableRecursiveReconciliation_ = source.disableRecursiveReconciliation_;
modifier_ = source.modifier_;
return *this;
}
void ParameterList::setModifier(
RCP<const ParameterListModifier> const& modifier_in)
{
modifier_ = modifier_in;
}
ParameterList& ParameterList::setParameters(const ParameterList& source)
{
for( ConstIterator i = source.begin(); i != source.end(); ++i ) {
......@@ -166,6 +176,29 @@ ParameterList& ParameterList::disableRecursiveValidation()
}
ParameterList& ParameterList::disableRecursiveModification()
{
disableRecursiveModification_ = true;
return *this;
}
ParameterList& ParameterList::disableRecursiveReconciliation()
{
disableRecursiveReconciliation_ = true;
return *this;
}
ParameterList& ParameterList::disableRecursiveAll()
{
this->disableRecursiveModification();
this->disableRecursiveValidation();
this->disableRecursiveReconciliation();
return *this;
}
void ParameterList::unused(std::ostream& os) const
{
for (ConstIterator i = this->begin(); i != this->end(); ++i) {
......@@ -272,6 +305,22 @@ ParameterList& ParameterList::sublist(
}
ParameterList& ParameterList::sublist(
const std::string& name_in, RCP<const ParameterListModifier> const& modifier_in,
const std::string& docString
)
{
bool alreadyExists = this->isParameter(name_in);
TEUCHOS_TEST_FOR_EXCEPTION_PURE_MSG(
alreadyExists, Exceptions::InvalidParameterName
,"The parameter "<<this->name()<<"->\""<<name_in<<"\" already exists."
);
ParameterList &subpl = this->sublist(name_in, false, docString);
subpl.setModifier(modifier_in);
return subpl;
}
const ParameterList& ParameterList::sublist(const std::string& name_in) const
{
typedef StringIndexedOrderedValueObjectContainerBase SIOVOCB;
......@@ -457,6 +506,96 @@ void ParameterList::validateParameters(
}
void ParameterList::modifyParameterList(ParameterList & valid_pl,
int const depth)
{
RCP<const ParameterListModifier> modifier;
if (nonnull(modifier = valid_pl.getModifier())) {
modifier->modify(*this, valid_pl);
}
ConstIterator itr;
for (itr = valid_pl.begin(); itr != valid_pl.end(); ++itr){
const std::string &entry_name = itr->first;
const ParameterEntry &entry = itr->second;
if (entry.isList() && depth > 0){
ParameterList &valid_pl_sublist = valid_pl.sublist(entry_name, true);
if(!valid_pl_sublist.disableRecursiveModification_){
const ParameterEntry *validEntry = this->getEntryPtr(entry_name);
TEUCHOS_TEST_FOR_EXCEPTION(
!validEntry, Exceptions::InvalidParameterName
,"Error, the parameter {name=\""<<entry_name<<"\","
"type=\""<<entry.getAny(false).typeName()<<"\""
",value=\""<<filterValueToString(entry)<<"\"}"
"\nin the parameter (sub)list \""<<this->name()<<"\""
"\nwas not found in the list of parameters during modification."
"\n\nThe parameters and types are:\n"
<<this->currentParametersString()
);
ParameterList &pl_sublist = this->sublist(entry_name, true);
pl_sublist.modifyParameterList(valid_pl_sublist, depth-1);
}
}
}
}
void ParameterList::reconcileParameterList(ParameterList & valid_pl,
const bool left_to_right)
{
// We do a breadth-first traversal of `valid_pl` and store references to all of the sublists
// in `valid_pl` in a deque with a matching deque for `this`.
std::deque<std::reference_wrapper<ParameterList>> refs, valid_refs, tmp, valid_tmp;
tmp.push_back(*this);
valid_tmp.push_back(valid_pl);
while (!valid_tmp.empty()){
ParameterList &cur_node = tmp.front();
ParameterList &valid_cur_node = valid_tmp.front();
tmp.pop_front();
valid_tmp.pop_front();
refs.push_back(cur_node);
valid_refs.push_back(valid_cur_node);
// Look for all sublists in valid_tmp
for (auto itr = valid_cur_node.begin(); itr != valid_cur_node.end(); ++itr){
const std::string &entry_name = itr->first;
if (valid_cur_node.isSublist(entry_name)){
const ParameterEntry &entry = itr->second;
ParameterList &valid_cur_node_sublist = valid_cur_node.sublist(entry_name);
if (!valid_cur_node_sublist.disableRecursiveReconciliation_){
TEUCHOS_TEST_FOR_EXCEPTION_PURE_MSG(
!cur_node.isSublist(entry_name), Exceptions::InvalidParameterName
,"Error, the parameter {name=\"" << entry_name <<"\","
"type=\"" << entry.getAny(false).typeName() << "\""
",value=\"" << filterValueToString(entry) << "\"}"
"\nin the parameter (sub)list \"" <<cur_node.name() << "\""
"\nwas not found in the list of parameters during reconciliation."
"\n\nThe parameters and types are:\n"
<<cur_node.currentParametersString()
);
if (left_to_right){
valid_tmp.push_back(valid_cur_node_sublist);
tmp.push_back(cur_node.sublist(entry_name));
} else{
valid_tmp.push_front(valid_cur_node_sublist);
tmp.push_front(cur_node.sublist(entry_name));
}
}
}
}
}
// We now apply the reconciliation from the bottom to the top of the parameter lists by
// traversing the deques from the back to the front.
RCP<const ParameterListModifier> modifier;
std::deque<std::reference_wrapper<ParameterList>>::reverse_iterator ref, valid_ref;
for(ref = refs.rbegin(), valid_ref = valid_refs.rbegin();
ref != refs.rend() && valid_ref != valid_refs.rend();
++ref, ++valid_ref){
if (nonnull(modifier = valid_ref->get().getModifier())) {
modifier->reconcile(ref->get());
}
}
}
void ParameterList::validateParametersAndSetDefaults(
ParameterList const& validParamList,
int const depth
......@@ -632,7 +771,6 @@ void ParameterList::validateMissingSublistMustExist(const std::string &baselist_
}
} // namespace Teuchos
......@@ -644,6 +782,9 @@ bool Teuchos::operator==( const ParameterList& list1, const ParameterList& list2
//if ( paramListName1 != paramListName2 ) {
// return false;
//}
if (!Teuchos::haveSameModifiers(list1, list2)){
return false;
}
ParameterList::ConstIterator itr1, itr2;
for(
itr1 = list1.begin(), itr2 = list2.begin();
......@@ -674,6 +815,33 @@ bool Teuchos::operator==( const ParameterList& list1, const ParameterList& list2
}
bool Teuchos::haveSameModifiers(const ParameterList &list1, const ParameterList &list2) {
// Check that the modifiers are the same
ParameterList::ConstIterator itr1, itr2;
for(
itr1 = list1.begin(), itr2 = list2.begin();
itr1 != list1.end() && itr2 != list2.end();
++itr1, ++itr2
)
{
const Teuchos::RCP<const ParameterListModifier> &modifier1 = list1.getModifier();
const Teuchos::RCP<const ParameterListModifier> &modifier2 = list2.getModifier();
if( modifier1 != modifier2 ) {
return false;
}
const Teuchos::ParameterEntry &entry1 = itr1->second;
const Teuchos::ParameterEntry &entry2 = itr2->second;
if (entry1.isList() && entry2.isList()){
if ( !haveSameModifiers( Teuchos::getValue<ParameterList>(entry1),
Teuchos::getValue<ParameterList>(entry2) ) ){
return false;
}
}
}
return true;
}
bool Teuchos::haveSameValues( const ParameterList& list1, const ParameterList& list2, bool verbose )
{
// Check that the top-level names of the two parameter lists are the same
......
......@@ -48,6 +48,7 @@
*/
#include "Teuchos_ParameterListExceptions.hpp"
#include "Teuchos_ParameterListModifier.hpp"
#include "Teuchos_ParameterEntry.hpp"
#include "Teuchos_StringIndexedOrderedValueObjectContainer.hpp"
#include "Teuchos_Assert.hpp"
......@@ -172,10 +173,11 @@ public:
//@{
//! Constructor
ParameterList();
ParameterList() = default;
//! Constructor that names the entire parameter list.
ParameterList(const std::string &name);
ParameterList(const std::string &name,
RCP<const ParameterListModifier> const& modifier = null);
//! Copy constructor
ParameterList(const ParameterList& source);
......@@ -198,6 +200,10 @@ public:
/// \note This also replaces the name returned by <tt>this->name()</tt>
ParameterList& operator= (const ParameterList& source);
void setModifier(
RCP<const ParameterListModifier> const& modifier
);
/** Set the parameters in <tt>source</tt>.
*
* This function will set the parameters and sublists from
......@@ -228,6 +234,35 @@ public:
*/
ParameterList& disableRecursiveValidation();
/** Disallow recursive modification when this sublist is used in a modified
* parameter list.
*
* This function should be called when setting a sublist in a modified
* parameter list which is broken off to be passed to another object.
* The other object should modify its own list. The parameter list can
* still be modified using a direct call to its modify method.
*/
ParameterList& disableRecursiveModification();
/** Disallow recursive reconciliation when this sublist is used in a
* reconciled parameter list.
*
* This function should be called when setting a sublist in a reconciled
* parameter list which is broken off to be passed to another object.
* The other object should reconcile its own list. The parameter list can
* still be reconciled using a direct call to its reconcile method.
*/
ParameterList& disableRecursiveReconciliation();
/** Disallow all recursive modification, validation, and reconciliation when
* this sublist is used in a parameter list.
*
* This function should be called when setting a sublist in a
* parameter list which is broken off to be passed to another object.
* The other object should handle its own list.
*/
ParameterList& disableRecursiveAll();
/*! \brief Set a parameter whose value has type T.
\param name [in] The parameter's name.
......@@ -283,6 +318,15 @@ public:
*/
ParameterList& setEntry(const std::string& name, const ParameterEntry& entry);
/** \brief Recursively attach a validator to parameters of type T.
*
* \param depth [in] Determines the number of levels of depth that the validator attachment
* will recurse into.
*/
template<typename T>
void recursivelySetValidator(RCP<const ParameterEntryValidator> const& validator,
int const depth = 1000);
//@}
//! @name Get Functions
//@{
......@@ -435,6 +479,9 @@ public:
* it exists. */
inline RCP<const ParameterEntry> getEntryRCP(const std::string& name) const;
//! \brief Return the optional modifier object
inline RCP<const ParameterListModifier> getModifier() const;
//@}
//! @name Parameter removal functions
......@@ -468,8 +515,17 @@ public:
* thrown.
*/
ParameterList& sublist(
const std::string& name, bool mustAlreadyExist = false
,const std::string& docString = ""
const std::string& name, bool mustAlreadyExist = false,
const std::string& docString = ""
);
/*! \brief Creates an empty sublist with an optional \c modifier and returns
* a reference to the sublist \c name. If a list or parameter with the same
* name already exists then an std::exception is thrown.
*/
ParameterList& sublist(
const std::string& name, RCP<const ParameterListModifier> const& modifier,
const std::string& docString = ""
);
/*! \brief Return a const reference to an existing sublist \c name. If the
......@@ -662,6 +718,35 @@ public:
int const depth = 1000
);
/** \brief Modify the valid parameter list prior to validation.
*
* \param validModifiedParamList [in,out] The parameter list used as a template for validation.
*
* \param depth [in] Determines the number of levels of depth that the
* modification will recurse into. A value of <tt>depth=0</tt> means that
* only the top level parameters and sublists will be checked. Default:
* <tt>depth = large number</tt>.
*
* We loop over the valid parameter list in this modification routine. This routine
* adds and/or removes fields in the valid parameter list to match the structure of the
* parameter list about to be validated. After completion, both parameter lists should
* have the same fields or else an error will be thrown during validation.
*/
void modifyParameterList(ParameterList &validParamList, int const depth = 1000);
/** \brief Reconcile a parameter list after validation
*
* \param validParamList [in,out] The parameter list used as a template for validation.
*
* \param left_to_right [in] Sweep through the parameter list tree from left to right.
*
* We loop through the valid parameter list in reverse breadth-first order in this reconciliation
* routine. This routine assumes that the reconciliation routine won't create new sublists as it
* traverses the parameter list.
*/
void reconcileParameterList(ParameterList &validParamList,
const bool left_to_right = true);
//@}
private: // Functions
......@@ -691,7 +776,7 @@ private: // Functions
private: // Data members
//! Name of the (sub)list
std::string name_;
std::string name_ = "ANONYMOUS";
//! Parameter list
//use pragmas to disable some false-positive warnings for windows sharedlibs export
......@@ -705,8 +790,15 @@ private: // Data members
//#endif
//! Validate into list or not
bool disableRecursiveValidation_;
bool disableRecursiveValidation_ = false;
//! Modify into list or not
bool disableRecursiveModification_ = false;
//! Reconcile into list or not
bool disableRecursiveReconciliation_ = false;
RCP<const ParameterListModifier> modifier_ = null;
};
......@@ -796,6 +888,19 @@ bool operator!=( const ParameterList& list1, const ParameterList& list2 )
}
/** \brief Returns true if two parameter lists have the same modifiers.
*
* Recursively compares the modifiers in two parameter lists for equality.
*
* \relates ParameterList
*/
/// Return true if a modified parameter list has the same modifiers as the modified parameter
/// list being used as input.
TEUCHOSPARAMETERLIST_LIB_DLL_EXPORT bool haveSameModifiers (const ParameterList& list1,
const ParameterList& list2);
/** \brief Returns true if two parameter lists have the same values.
*
* Two parameter lists may have the same values but may not be identical. For
......@@ -909,6 +1014,25 @@ ParameterList& ParameterList::setEntry(std::string const& name_in, ParameterEntr
}
template<typename T>
void ParameterList::recursivelySetValidator(
RCP<const ParameterEntryValidator> const& validator, int const depth)
{
ConstIterator itr;
for (itr = this->begin(); itr != this->end(); ++itr){
const std::string &entry_name = itr->first;
if (this->isSublist(entry_name) && depth > 0){
this->sublist(entry_name).recursivelySetValidator<T>(validator, depth - 1);
} else{
ParameterEntry *entry = this->getEntryPtr(entry_name);
if (entry->isType<T>()){
entry->setValidator(validator);
}
}
}
}
// Get functions
......@@ -1064,6 +1188,11 @@ ParameterList::getEntryRCP(const std::string& name_in) const
}
inline RCP<const ParameterListModifier>
ParameterList::getModifier() const
{ return modifier_; }
// Attribute Functions
......@@ -1399,6 +1528,42 @@ Array<T> getArrayFromStringParameter(
}
/*! \relates ParameterList
* \brief Replace a parameter with an array containing the parameter.
*
* \param paramName [in] The name of the parameter to be placed in an array.
*
* \param newName [in] The name of the new parameter containing the old
* parameter in an array.
*
* \param pl [in,out] The parameter list \a pl containing \a paramName.
*
* \returns Returns <tt>true</tt> if the parameter \a paramName exists in \a pl.
*/
template<typename T>
bool replaceParameterWithArray(const std::string &paramName, const std::string &newName,
ParameterList &pl)
{
bool param_exists = false;
bool overwrite = false;
if (paramName == newName){
overwrite = true;
}
if (pl.isParameter(paramName)){
param_exists = true;
TEUCHOS_TEST_FOR_EXCEPTION(!pl.isType<T>(paramName), std::logic_error,
"The parameter " << paramName << " is not of type " << typeid(T).name());
TEUCHOS_TEST_FOR_EXCEPTION(pl.isParameter(newName) && !overwrite,
std::logic_error, "The parameter " << newName << " already exists in this "
"parameter list.");
Array<T> params = tuple<T>(pl.get<T>(paramName));
pl.remove(paramName);
pl.set(newName, params);
}
return param_exists;
}
/*! \relates ParameterList
\brief Return a RCP to a sublist in another RCP-ed parameter list.
*/
......
// @HEADER
// ***********************************************************************
//
// Teuchos: Common Tools Package
// Copyright (2004) Sandia Corporation
//
// Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
// license for use of this work by or on behalf of the U.S. Government.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the Corporation nor the names of the
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Questions? Contact Michael A. Heroux (maherou@sandia.gov)
//
// ***********************************************************************
// @HEADER
#include "Teuchos_ParameterListModifier.hpp"
#include "Teuchos_ParameterList.hpp"
#include "Teuchos_StrUtils.hpp"
namespace Teuchos {
// Constructors and/or destructors
ParameterListModifier::ParameterListModifier(const std::string &name_in)
:name_(name_in)
{}
ParameterListModifier::~ParameterListModifier()
{}
void ParameterListModifier::printDoc(std::string const& docString, std::ostream &out) const
{
StrUtils::printLines(out,"# ",docString);
out << "# Modifier Used: " << name_ << std::endl;
}
Array<std::string> ParameterListModifier::findMatchingBaseNames(const ParameterList &pl,
const std::string &base_name, const bool &find_parameters, const bool &find_sublists) const
{
Array<std::string> matches(0);
ParameterList::ConstIterator itr;