Commit 8566756a authored by Floris Berendsen's avatar Floris Berendsen
Browse files

ENH: WIP: refactoring of Overlord and ComponentSelector

parent a58c4909
......@@ -59,8 +59,11 @@ namespace selx
virtual int AcceptConnectionFrom(ComponentBase*) = 0;
/** if there is any failed criterion, return false (like a short - circuit AND) */
//TODO deprecate:
bool MeetsCriteria(const CriteriaType &criteria);
bool MeetsCriterionBase(const CriterionType &criterion);
virtual bool MeetsCriterion(const CriterionType &criterion) = 0;
protected:
virtual bool HasAcceptingInterface(const char *) = 0;
......
......@@ -52,24 +52,25 @@ public:
/** Convenient typedefs. */
typedef ComponentBase::Pointer ComponentBasePointer;
typedef ComponentBase::CriteriaType CriteriaType;
typedef ComponentBase::CriterionType CriterionType;
typedef std::list< ComponentBasePointer > ComponentListType;
typedef ComponentListType::size_type NumberOfComponentsType;
/** set selection criteria for possibleComponents*/
void Initialize();
void SetCriteria(const CriteriaType &criteria);
/** Narrow selection criteria*/
void AddCriteria(const CriteriaType &criteria);
NumberOfComponentsType UpdatePossibleComponents(void);
void AddCriterion(const CriterionType &criterion);
/** Check for multiple versus 1 or 0 components*/
bool HasMultipleComponents(void);
/** Return Component or Nullptr*/
ComponentBasePointer GetComponent(void);
protected:
mutable CriteriaType m_Criteria;
mutable ComponentListType m_PossibleComponents;
CriteriaType m_Criteria;
ComponentListType m_PossibleComponents;
ComponentSelector();
~ComponentSelector();
......
......@@ -54,6 +54,7 @@ namespace selx
typedef ComponentSelector::Pointer ComponentSelectorPointer;
typedef Blueprint BlueprintType;
typedef BlueprintType::ComponentNameType ComponentNameType;
typedef BlueprintType::ComponentNamesType ComponentNamesType;
typedef std::map< ComponentNameType, ComponentSelectorPointer> ComponentSelectorContainerType;
typedef ComponentSelectorContainerType::iterator ComponentSelectorIteratorType;
......@@ -98,11 +99,13 @@ namespace selx
public: // temporarily from private to public during refactoring SuperElastixFilter.
void ApplyNodeConfiguration();
void ApplyConnectionConfiguration();
bool UpdateSelectors();
//bool UpdateSelectors();
bool ConnectComponents();
bool FindAfterRegistration();
bool FindRunRegistration();
private:
ComponentNamesType GetNonUniqueComponentNames();
bool ConnectSources();
bool ConnectSinks();
bool RunRegistrations();
......
......@@ -23,65 +23,49 @@ namespace selx
{
bool ComponentBase::MeetsCriteria(const CriteriaType &criteria)
bool ComponentBase::MeetsCriterionBase(const CriterionType &criterion)
{
bool hasUndefinedCriteria(false);
bool meetsCriteria(true);
for (CriteriaType::const_iterator it = criteria.begin(); it != criteria.cend(); ++it)
if (criterion.first == "NameOfClass")
{
if (it->first == "NameOfClass")
if (criterion.second.size() != 1)
{
if (it->second.size() != 1)
{
itkExceptionMacro("The criterion NameOfClass may have only 1 value");
}
auto temp = this->GetNameOfClass();
if (it->second[0] != this->GetNameOfClass())
{
meetsCriteria = false;
//break;
return false; //if there is any failed criterion, return false (like a short-circuit AND)
}
itkExceptionMacro("The criterion NameOfClass may have only 1 value");
}
return (criterion.second[0] == this->GetNameOfClass());
}
else if (it->first == "HasAcceptingInterface")
else if (criterion.first == "HasAcceptingInterface")
{
for (const auto & value : criterion.second)
{
ParameterValueType::const_iterator valueIt;
const ParameterValueType::const_iterator valueItEnd = it->second.cend();
for (valueIt = it->second.cbegin(); valueIt != valueItEnd; ++valueIt)
if (this->HasAcceptingInterface(value.c_str()) == false)
{
if (this->HasAcceptingInterface(valueIt->c_str()) == false)
{
meetsCriteria = false;
//break;
return false; //if there is any failed criterion, return false (like a short-circuit AND)
}
return false; //if there is any failed criterion, return false (like a short-circuit AND)
}
}
else if (it->first =="HasProvidingInterface")
return true;
}
else if (criterion.first == "HasProvidingInterface")
{
for (const auto & value : criterion.second)
{
ParameterValueType::const_iterator valueIt;
const ParameterValueType::const_iterator valueItEnd = it->second.cend();
for (valueIt = it->second.cbegin(); valueIt != valueItEnd; ++valueIt){
if (this->HasProvidingInterface(valueIt->c_str()) == false)
{
meetsCriteria = false;
//break;
return false; //if there is any failed criterion, return false (like a short-circuit AND)
}
if (this->HasProvidingInterface(value.c_str()) == false)
{
return false; //if there is any failed criterion, return false (like a short-circuit AND)
}
}
else if (this->MeetsCriterion(CriterionType(it->first, it->second)) == false)
{
meetsCriteria = false;
return false; //if there is any failed criterion, return false (like a short-circuit AND)
}
return true;
}
return meetsCriteria;
// else pass criterion to derived Component
return this->MeetsCriterion(criterion);
}
//TODO deprecate:
bool ComponentBase::MeetsCriteria(const CriteriaType &criteria)
{
return false;
}
} // end namespace selx
......
......@@ -26,81 +26,34 @@ namespace selx
ComponentSelector::ComponentSelector()
{
this->m_PossibleComponents.clear();
}
ComponentSelector::~ComponentSelector()
{
}
std::list< itk::LightObject::Pointer > allobjects =
itk::ObjectFactoryBase::CreateAllInstance("ComponentBase");
void ComponentSelector::Initialize()
{
std::list< itk::LightObject::Pointer > allobjects =
itk::ObjectFactoryBase::CreateAllInstance("ComponentBase");
for (std::list< itk::LightObject::Pointer >::iterator i = allobjects.begin();
i != allobjects.end(); ++i)
{
ComponentBase *io =
dynamic_cast<ComponentBase *>(i->GetPointer());
if (io)
for (std::list< itk::LightObject::Pointer >::iterator i = allobjects.begin();
i != allobjects.end(); ++i)
{
this->m_PossibleComponents.push_back(io);
ComponentBase *io =
dynamic_cast<ComponentBase *>(i->GetPointer());
if (io)
{
this->m_PossibleComponents.push_back(io);
}
}
}
}
void ComponentSelector::SetCriteria(const CriteriaType &criteria)
{
this->Initialize();
this->m_Criteria = criteria;
this->Modified();
}
ComponentSelector::~ComponentSelector()
{
}
void ComponentSelector::AddCriteria(const CriteriaType &criteria)
void ComponentSelector::AddCriterion(const CriterionType &criterion)
{
this->m_Criteria.insert(criteria.begin(), criteria.end());
this->Modified();
this->m_PossibleComponents.remove_if([&](ComponentBasePointer component){ return !component->MeetsCriterionBase(criterion); });
}
// a predicate implemented as a class:
//struct FailsCriteria {
// bool operator() (const ComponentBasePointer& component) { return !component->MeetsCriteria(this->m_Criteria) }
//};
ComponentSelector::NumberOfComponentsType ComponentSelector::UpdatePossibleComponents()
{
// Check each possible component if it meets the criteria
// Using a Lambda function.
this->m_PossibleComponents.remove_if([&](ComponentBasePointer component){ return !component->MeetsCriteria(this->m_Criteria); });
const int numberOfComponents = this->m_PossibleComponents.size();
if (numberOfComponents == 0)
{
//TODO report about m_Criteria
std::stringstream message;
message << "Too many criteria for component. There is no component in our database that fulfills this set of criteria: " << std::endl;
for (CriteriaType::const_iterator criterion = this->m_Criteria.begin(); criterion != this->m_Criteria.cend(); ++criterion)
{
message << " " << criterion->first << " : { ";
for (auto const & criterionValue : criterion->second) // auto&& preferred?
{
message << criterionValue << " ";
}
message << "}" << std::endl;
}
std::cout << message.str();
//TODO how does this work for strings?
itkExceptionMacro("Too many criteria for component ");
}
return numberOfComponents;
}
ComponentSelector::ComponentBasePointer ComponentSelector::GetComponent()
{
//TODO check if Modified
this->UpdatePossibleComponents();
//this->UpdatePossibleComponents();
if (this->m_PossibleComponents.size() == 1)
{
......@@ -111,6 +64,12 @@ ComponentSelector::ComponentBasePointer ComponentSelector::GetComponent()
return ITK_NULLPTR;
}
}
bool ComponentSelector::HasMultipleComponents()
{
return (this->m_PossibleComponents.size() > 1);
}
} // end namespace selx
//#endif
......@@ -31,56 +31,60 @@ namespace selx
{
if (!this->m_isConfigured)
{
this->m_isConfigured = true;
std::cout << "Applying Component Criteria" << std::endl;
this->ApplyNodeConfiguration();
std::cout << "Applying Component Settings" << std::endl;
this->m_allUniqueComponents = this->UpdateSelectors();
std::cout << "Based on Component Criteria unique components could " << (this->m_allUniqueComponents ? "" : "not ") << "be selected" << std::endl;
std::cout << "Applying Connection Settings" << std::endl;
this->ApplyConnectionConfiguration();
this->m_allUniqueComponents = this->UpdateSelectors();
std::cout << "By adding Connection Criteria unique components could " << (this->m_allUniqueComponents ? "" : "not ") << "be selected" << std::endl;
//TODO: make a while loop until stable:
// - propagate for each unique component the required interfaces to neighboring components
// - update selectors
ComponentNamesType nonUniqueComponentNames;
nonUniqueComponentNames = this->GetNonUniqueComponentNames();
std::cout << nonUniqueComponentNames.size() << " out of " << m_Blueprint->GetComponentNames().size() << " Components could not be uniquely selected" << std::endl << std::endl;
//if (allUniqueComponents)
//{
// isSuccess = this->ConnectComponents();
//}
std::cout << "Applying Connection Criteria" << std::endl;
this->ApplyConnectionConfiguration();
nonUniqueComponentNames = this->GetNonUniqueComponentNames();
std::cout << nonUniqueComponentNames.size() << " out of " << m_Blueprint->GetComponentNames().size() << " Components could not be uniquely selected" << std::endl << std::endl;
//std::cout << "Connecting Components: " << (isSuccess? "succeeded" : "failed") << std::endl;
//return isSuccess;
if (nonUniqueComponentNames.size() > 0)
{
this->m_allUniqueComponents = false;
std::cout << std::endl << "These Nodes need more criteria: " << std::endl;
for (const auto & nonUniqueComponentName : nonUniqueComponentNames)
{
std::cout << nonUniqueComponentName<< std::endl;
}
}
else
{
this->m_allUniqueComponents = true;
}
//TODO: make a while loop until stable:
// - for each unique component propagate the required interfaces to neighboring components as added criterion
this->m_isConfigured = true;
}
return this->m_allUniqueComponents;
}
bool Overlord::UpdateSelectors()
Overlord::ComponentNamesType Overlord::GetNonUniqueComponentNames()
{
bool allUniqueComponents = true;
ComponentNamesType nonUniqueComponentNames;
const Blueprint::ComponentNamesType componentNames = m_Blueprint->GetComponentNames();
for (auto const & name : componentNames)
{
ComponentSelector::NumberOfComponentsType numberOfComponents = this->m_ComponentSelectorContainer[name]->UpdatePossibleComponents();
{
// The current idea of the configuration setup is that the number of
// possible components at a node can only be reduced by adding criteria.
// If a node has 0 possible components, the configuration is aborted (with an exception)
// If all nodes have exactly 1 possible component, no more criteria are needed.
//
// Design consideration: should the exception be thrown by this->m_ComponentSelectorContainer[name]->UpdatePossibleComponents()?
// The (failing) criteria can be printed as well.
if (numberOfComponents > 1)
if (this->m_ComponentSelectorContainer[name]->HasMultipleComponents()==true)
{
allUniqueComponents = false;
//std::cout << "To select a component for blueprint node " << name << " more critatia are required" << std::endl;
nonUniqueComponentNames.push_back(name);
}
std::cout << "blueprint node " << name << " has selected " << numberOfComponents << " components" << std::endl;
}
return allUniqueComponents;
return nonUniqueComponentNames;
}
void Overlord::ApplyNodeConfiguration()
......@@ -102,16 +106,31 @@ namespace selx
// in turn, hold 1 (or more) component.
Blueprint::ComponentNamesType componentNames = this->m_Blueprint->GetComponentNames();
for (auto const & name : componentNames)
{
std::cout << " Blueprint Node: " << name << std::endl;
ComponentSelectorPointer currentComponentSelector = ComponentSelector::New();
Blueprint::ParameterMapType currentProperty = this->m_Blueprint->GetComponent(name);
currentComponentSelector->SetCriteria(currentProperty);
for (auto const & criterion : currentProperty)
{
std::cout << " " << criterion.first << ": " << criterion.second[0] << std::endl;
currentComponentSelector->AddCriterion(criterion);
}
if ((currentComponentSelector->HasMultipleComponents() == false) && (currentComponentSelector->GetComponent().IsNull()))
{
std::stringstream msg;
msg << "Too many criteria for Component " << name << std::endl;
std::runtime_error::runtime_error(msg.str());
}
// insert new element
this->m_ComponentSelectorContainer[name]=currentComponentSelector;
}
return;
}
void Overlord::ApplyConnectionConfiguration()
{
Blueprint::ComponentNamesType componentNames = this->m_Blueprint->GetComponentNames();
......@@ -123,16 +142,18 @@ namespace selx
Blueprint::ParameterMapType connectionProperties = this->m_Blueprint->GetConnection(name, outgoingName);
if (connectionProperties.count("NameOfInterface") > 0)
{
ComponentBase::CriteriaType additionalSourceCriteria;
additionalSourceCriteria.insert(ComponentBase::CriterionType("HasProvidingInterface", connectionProperties["NameOfInterface"]));
ComponentBase::CriteriaType additionalTargetCriteria;
additionalTargetCriteria.insert(ComponentBase::CriterionType("HasAcceptingInterface", connectionProperties["NameOfInterface"]));
this->m_ComponentSelectorContainer[name]->AddCriteria(additionalSourceCriteria);
this->m_ComponentSelectorContainer[outgoingName]->AddCriteria(additionalTargetCriteria);
this->m_ComponentSelectorContainer[name]->AddCriterion({ "HasProvidingInterface", connectionProperties["NameOfInterface"] });
this->m_ComponentSelectorContainer[outgoingName]->AddCriterion({ "HasAcceptingInterface", connectionProperties["NameOfInterface"] });
std::cout << " Blueprint Node: " << name << std::endl << " HasProvidingInterface " << connectionProperties["NameOfInterface"][0] << std::endl;
std::cout << " Blueprint Node: " << outgoingName << std::endl << " HasAcceptingInterface " << connectionProperties["NameOfInterface"][0] << std::endl;
}
}
if ((this->m_ComponentSelectorContainer[name]->HasMultipleComponents() == false) && (this->m_ComponentSelectorContainer[name]->GetComponent().IsNull()))
{
std::stringstream msg;
msg << "Too many criteria for Component " << name << std::endl;
std::runtime_error::runtime_error(msg.str());
}
}
return;
......
......@@ -38,7 +38,7 @@ public:
typedef std::list< itk::LightObject::Pointer > RegisteredObjectsContainerType;
typedef ComponentBase ComponentType;
typedef ComponentBase::CriteriaType CriteriaType;
//typedef ComponentBase::CriteriaType CriteriaType;
typedef ComponentBase::CriterionType CriterionType;
typedef ComponentBase::ParameterValueType ParameterValueType;
//typedef std::map<std::string, std::string> CriteriaType;
......@@ -95,11 +95,11 @@ TEST_F(ComponentFactoryTest, SetEmptyCriteria)
EXPECT_NO_THROW(ComponentFactory<TransformComponent1>::RegisterOneFactory());
EXPECT_NO_THROW(ComponentFactory<MetricComponent1>::RegisterOneFactory());
CriteriaType emptyCriteria; // = CriteriaType();
CriterionType emptyCriterion; // = CriterionType();
ASSERT_NO_THROW(Node1 = ComponentSelector::New());
EXPECT_NO_THROW(Node1->SetCriteria(emptyCriteria));
EXPECT_NO_THROW(Node1->AddCriterion(emptyCriterion));
ComponentType::Pointer Node1Component;
EXPECT_NO_THROW(Node1Component = Node1->GetComponent());
......@@ -112,11 +112,10 @@ TEST_F(ComponentFactoryTest, SetSufficientCriteria)
EXPECT_NO_THROW(ComponentFactory<TransformComponent1>::RegisterOneFactory());
EXPECT_NO_THROW(ComponentFactory<MetricComponent1>::RegisterOneFactory());
CriteriaType criteria2;
criteria2["ComponentInput"] = {"Transform"};
CriterionType criterion2 = { "ComponentInput", { "Transform" } };
ASSERT_NO_THROW(Node2 = ComponentSelector::New());
Node2->SetCriteria(criteria2);
ASSERT_NO_THROW(Node2->AddCriterion(criterion2));
ComponentType::Pointer Node2Component;
EXPECT_NO_THROW(Node2Component = Node2->GetComponent());
......@@ -131,18 +130,26 @@ TEST_F(ComponentFactoryTest, AddCriteria)
EXPECT_NO_THROW(ComponentFactory<TransformComponent1>::RegisterOneFactory());
EXPECT_NO_THROW(ComponentFactory<MetricComponent1>::RegisterOneFactory());
CriteriaType emptyCriteria;
CriteriaType criteria1;
criteria1["ComponentOutput"] = { "Transform" };
CriterionType nonSelectiveCriterion("ComponentProperty", { "SomeProperty" });
CriterionType criterion1({ "ComponentOutput", { "Transform" } });
Node1 = ComponentSelector::New();
Node1->SetCriteria(emptyCriteria);
EXPECT_NO_THROW(Node1->AddCriteria(criteria1));
EXPECT_NO_THROW(Node1->AddCriterion(nonSelectiveCriterion));
ComponentType::Pointer Node1Component;
EXPECT_TRUE(Node1->HasMultipleComponents());
EXPECT_NO_THROW(Node1Component = Node1->GetComponent());
//Unsufficient criteria means no Component was selected."
EXPECT_TRUE(Node1Component.IsNull());
EXPECT_NO_THROW(Node1->AddCriterion(criterion1));
EXPECT_NO_THROW(Node1Component = Node1->GetComponent());
//Sufficient criteria means one Component was selected."
EXPECT_FALSE(Node1->HasMultipleComponents());
EXPECT_FALSE(Node1Component.IsNull());
//Based on the criteria TransformComponent1 should be selected
EXPECT_STREQ(Node1Component->GetNameOfClass(), "TransformComponent1");
......@@ -162,38 +169,34 @@ TEST_F(ComponentFactoryTest, InterfacedObjects)
// " 6 Component objects available to the Overlord."
EXPECT_EQ(registeredComponents.size(), 6);
CriteriaType criteria3;
criteria3["HasAcceptingInterface"] = { "MetricDerivativeInterface" };
CriterionType criterion3({ "HasAcceptingInterface", { "MetricDerivativeInterface" } });
NodePointer Node3 = ComponentSelector::New();
Node3->SetCriteria(criteria3);
Node3->AddCriterion(criterion3);
ComponentType::Pointer Node3Component;
EXPECT_NO_THROW(Node3Component = Node3->GetComponent());
EXPECT_STREQ(Node3Component->GetNameOfClass(), "GDOptimizer3rdPartyComponent");
CriteriaType criteria4;
criteria4["NameOfClass"] = { "GDOptimizer4thPartyComponent" };
CriterionType criterion4({ "NameOfClass", { "GDOptimizer4thPartyComponent" } });
NodePointer Node4 = ComponentSelector::New();
Node4->SetCriteria(criteria4);
Node4->AddCriterion(criterion4);
ComponentType::Pointer Node4Component;
EXPECT_NO_THROW(Node4Component = Node4->GetComponent());
EXPECT_STREQ(Node4Component->GetNameOfClass(), "GDOptimizer4thPartyComponent");
CriteriaType criteria5;
criteria5["HasProvidingInterface"] = { "MetricDerivativeInterface" };
CriterionType criterion5({ "HasProvidingInterface", { "MetricDerivativeInterface" } });
NodePointer Node5 = ComponentSelector::New();
Node5->SetCriteria(criteria5);
Node5->AddCriterion(criterion5);
ComponentType::Pointer Node5Component;
EXPECT_NO_THROW(Node5Component = Node5->GetComponent());
EXPECT_STREQ(Node5Component->GetNameOfClass(), "SSDMetric3rdPartyComponent");
CriteriaType criteria6;
criteria6["NameOfClass"] = { "SSDMetric4thPartyComponent" };
CriterionType criterion6({ "NameOfClass", { "SSDMetric4thPartyComponent" } });
NodePointer Node6 = ComponentSelector::New();
Node6->SetCriteria(criteria6);
Node6->AddCriterion(criterion6);
ComponentType::Pointer Node6Component;
EXPECT_NO_THROW(Node6Component = Node6->GetComponent());
EXPECT_STREQ(Node6Component->GetNameOfClass(), "SSDMetric4thPartyComponent");
......@@ -213,15 +216,15 @@ TEST_F(ComponentFactoryTest, UnknownComponent)
EXPECT_NO_THROW(ComponentFactory<SSDMetric4thPartyComponent>::RegisterOneFactory());
// Setup the criteria for a component that does not exist in our data base
CriteriaType criteria;
criteria["NameOfClass"] = { "DoYouHaveThisComponent?" };
// Setup the criterion for a component that does not exist in our data base
CriterionType criterion({ "NameOfClass", { "DoYouHaveThisComponent?" } });
NodePointer Node = ComponentSelector::New();
Node->SetCriteria(criteria);
Node->AddCriterion(criterion);
ComponentType::Pointer NodeComponent;
// we expect and exception here
EXPECT_THROW(NodeComponent = Node->GetComponent(), itk::ExceptionObject);
// we expect 0 components
EXPECT_FALSE(Node->HasMultipleComponents());
EXPECT_TRUE(Node->GetComponent().IsNull());
}
} // namespace selx
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment