Commit 930d3ede authored by Floris Berendsen's avatar Floris Berendsen
Browse files

ENH: Cleanup: create Overlord->PropagateConnectionsWithUniqueComponents()

and replace InterfaceName<> by Properties<> in InterfaceTraits.h
parent 301e21ec
......@@ -24,15 +24,21 @@
// In the core we could type these names directly as literal cstrings, but this is sensitive to typos.
// By these definitions tools such as visual assist will auto-complete when typing keys::HasP...
#ifndef selxKeys_h
#define selxKeys_h
namespace selx
{
namespace keys
{
const char * const NameOfInterface = "NameOfInterface"; // Each Interface has a sting name
const char * const HasProvidingInterface = "HasProvidingInterface"; // Checks component (base class) if the interface is present
const char * const HasAcceptingInterface = "HasAcceptingInterface"; // Checks component (base class) if the interface is present
const char * const Dimensionality = "Dimensionality"; // Template int parameter
const char * const PixelType = "PixelType"; // Template POD parameter
const char * const InternalComputationValueType = "InternalComputationValueType"; // Template POD parameter for transforms or optimizers etc.
const char * const SourceInterface = "SourceInterface"; // Special interface that connects to the outside of the SuperElastixFilter
const char * const SinkInterface = "SinkInterface"; // Special interface that connects to the outside of the SuperElastixFilter
const char * const RegistrationControllerStartInterface = "RegistrationControllerStartInterface"; //Special interface by which all algorithms are started
}
}
#endif //selxKeys_h
/*=========================================================================
*
* Copyright Leiden University Medical Center, Erasmus University Medical
* Center and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
#ifndef ComponentTraits_h
#define ComponentTraits_h
#include "selxGDOptimizer3rdPartyComponent.h"
namespace selx
{
// Traits to get printable interface name
// default implementation
template< typename T >
struct ComponentTraits
{
//static_assert(false, "Please implement a template specialization for the appropriate Component");
// I would like to be it a real Trait but this is not allowed:
//static const char* Name = "ERROR: Please implement Name";
//a static data member with an in - class initializer must have non - volatile const integral type
static const char * GetDescription()
{
return "ERROR: Please implement Description";
}
static const char * GetName()
{
return "ERROR: Please implement Name";
}
};
// a specialization for each type of those you want to support
// and don't like the string returned by typeid
template< >
struct InterfaceName< GDOptimizer3rdPartyComponent >
{
static const char * GetDescription()
{
return "GD Optimizer 3rdParty Component";
}
static const char * GetName()
{
return "GDOptimizer3rdPartyComponent";
}
};
} // end namespace selx
#endif // #define ComponentTraits_h
......@@ -25,6 +25,7 @@
#include "selxInterfaces.h"
#include "selxStaticErrorMessageRevealT.h"
#include "selxPodString.h"
#include "selxKeys.h"
//#include <type_traits>
namespace selx
......@@ -34,261 +35,230 @@ namespace selx
// In our toolbox for each InterfaceType it is required to implement InterfaceName<InterfaceType>::Get()
// If one omits this, the implementation falls back to the default implementation that provides a compiler error message including the InterfaceType name for which the specialization is missing.
template< typename T >
struct InterfaceName
struct Properties
{
static_assert(StaticErrorMessageRevealT<T>::False, "Please Implement Properties<InterfaceType> for this InterfaceType");
};
// Properties<T>::Get() should return the same name no matter whether T is an acceptor or provider interface.
template< typename InterfaceType >
struct Properties< InterfaceAcceptor< InterfaceType >>
{
static_assert(StaticErrorMessageRevealT<T>::False, "Please Implement InterfaceName<InterfaceType> for this InterfaceType");
static const std::map<std::string, std::string> Get()
{
return Properties< InterfaceType >::Get();
}
};
// The specializations for each type of Interface supported by the toolbox
template< >
struct InterfaceName< MetricValueInterface >
struct Properties< MetricValueInterface >
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "MetricValueInterface";
return{ { keys::NameOfInterface, "MetricValueInterface" } };
}
};
template< >
struct InterfaceName< MetricDerivativeInterface >
struct Properties< MetricDerivativeInterface >
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "MetricDerivativeInterface";
return{ { keys::NameOfInterface, "MetricDerivativeInterface" } };
}
};
template< >
struct InterfaceName< OptimizerUpdateInterface >
struct Properties< OptimizerUpdateInterface >
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "OptimizerUpdateInterface";
return{ { keys::NameOfInterface, "OptimizerUpdateInterface" } };
}
};
template< >
struct InterfaceName< ConflictinUpdateInterface >
struct Properties< ConflictinUpdateInterface >
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "ConflictinUpdateInterface";
return{ { keys::NameOfInterface, "ConflictinUpdateInterface" } };
}
};
template< >
struct InterfaceName< TransformedImageInterface >
struct Properties< TransformedImageInterface >
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "TransformedImageInterface";
return{ { keys::NameOfInterface, "TransformedImageInterface" } };
}
};
// InterfaceName<T>::Get() should return "itkImageSourceInterface" no matter over which arguments itkImageSourceInterface is templated
template< int D, class TPixel >
struct InterfaceName< itkImageInterface< D, TPixel >>
struct Properties< itkImageInterface< D, TPixel >>
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "itkImageInterface";
return{ { keys::NameOfInterface, "itkImageInterface" }, { keys::Dimensionality, std::to_string(D) }, { keys::PixelType, PodString<TPixel>::Get() } };
}
};
template< int D, class TPixel >
struct InterfaceName< itkImageFixedInterface< D, TPixel >>
struct Properties< itkImageFixedInterface< D, TPixel >>
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "itkImageFixedInterface";
return{ { keys::NameOfInterface, "itkImageFixedInterface" }, { keys::Dimensionality, std::to_string(D) }, { keys::PixelType, PodString<TPixel>::Get() } };
}
};
template< int D >
struct InterfaceName< itkImageDomainFixedInterface< D >>
struct Properties< itkImageDomainFixedInterface< D >>
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "itkImageDomainFixedInterface";
return{ { keys::NameOfInterface, "itkImageDomainFixedInterface" }, { keys::Dimensionality, std::to_string(D) } };
}
};
template< int D, class TPixel >
struct InterfaceName< itkImageMovingInterface< D, TPixel >>
struct Properties< itkImageMovingInterface< D, TPixel >>
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "itkImageMovingInterface";
return{ { keys::NameOfInterface, "itkImageMovingInterface" }, { keys::Dimensionality, std::to_string(D) }, { keys::PixelType, PodString<TPixel>::Get() } };
}
};
template< int D, class TPixel >
struct InterfaceName< DisplacementFieldItkImageSourceInterface< D, TPixel >>
struct Properties< DisplacementFieldItkImageSourceInterface< D, TPixel >>
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "DisplacementFieldItkImageSourceInterface";
return{ { keys::NameOfInterface, "DisplacementFieldItkImageSourceInterface" }, { keys::Dimensionality, std::to_string(D) }, { keys::PixelType, PodString<TPixel>::Get() } };
}
};
template< int D, class TPixel >
struct InterfaceName< itkMeshInterface< D, TPixel >>
struct Properties< itkMeshInterface< D, TPixel >>
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "itkMeshInterface";
return{ { keys::NameOfInterface, "itkMeshInterface" }, { keys::Dimensionality, std::to_string(D) }, { keys::PixelType, PodString<TPixel>::Get() } };
}
};
template< >
struct InterfaceName< SourceInterface >
struct Properties< SourceInterface >
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "SourceInterface";
return{ { keys::NameOfInterface, "SourceInterface" } };
}
};
template< >
struct InterfaceName< SinkInterface >
struct Properties< SinkInterface >
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "SinkInterface";
return{ { keys::NameOfInterface, "SinkInterface" } };
}
};
template< int D, class TPixel >
struct InterfaceName< itkMetricv4Interface< D, TPixel >>
struct Properties< itkMetricv4Interface< D, TPixel >>
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "itkMetricv4Interface";
return{ { keys::NameOfInterface, "itkMetricv4Interface" }, { keys::Dimensionality, std::to_string(D) }, { keys::PixelType, PodString<TPixel>::Get() } };
}
};
template< class InternalComputationValueType >
struct InterfaceName< itkOptimizerv4Interface< InternalComputationValueType >>
struct Properties< itkOptimizerv4Interface< InternalComputationValueType >>
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "itkOptimizerv4Interface";
return{ { keys::NameOfInterface, "itkOptimizerv4Interface" }, { keys::InternalComputationValueType, PodString<InternalComputationValueType>::Get() } };
}
};
template< class InternalComputationValueType, int D >
struct InterfaceName< itkTransformInterface< InternalComputationValueType, D >>
struct Properties< itkTransformInterface< InternalComputationValueType, D >>
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "itkTransformInterface";
return{ { keys::NameOfInterface, "itkTransformInterface" }, { keys::Dimensionality, std::to_string(D) } };
}
};
template< class F, class M >
struct InterfaceName< elastixTransformParameterObjectInterface< F, M >>
struct Properties< elastixTransformParameterObjectInterface< F, M >>
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "elastixTransformParameterObjectInterface";
return{ { keys::NameOfInterface, "elastixTransformParameterObjectInterface" } }; // TODO map F and M to strings?
}
};
template< >
struct InterfaceName< RegistrationControllerStartInterface >
struct Properties< RegistrationControllerStartInterface >
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "RegistrationControllerStartInterface";
return{ { keys::NameOfInterface, "RegistrationControllerStartInterface" } };
}
};
template< >
struct InterfaceName< RunRegistrationInterface >
struct Properties< RunRegistrationInterface >
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "RunRegistrationInterface";
return{ { keys::NameOfInterface, "RunRegistrationInterface" } };
}
};
template< >
struct InterfaceName< AfterRegistrationInterface >
struct Properties< AfterRegistrationInterface >
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "AfterRegistrationInterface";
return{ { keys::NameOfInterface, "AfterRegistrationInterface" } };
}
};
template< >
struct InterfaceName< ReconnectTransformInterface >
struct Properties< ReconnectTransformInterface >
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "ReconnectTransformInterface";
return{ { keys::NameOfInterface, "ReconnectTransformInterface" } };
}
};
template< class InternalComputationValueType, int D >
struct InterfaceName< itkTransformParametersAdaptorsContainerInterface< InternalComputationValueType, D >>
struct Properties< itkTransformParametersAdaptorsContainerInterface< InternalComputationValueType, D >>
{
static const char * Get()
static const std::map<std::string, std::string> Get()
{
return "itkTransformParametersAdaptorsContainerInterface";
return{ { keys::NameOfInterface, "itkTransformParametersAdaptorsContainerInterface" }, { keys::InternalComputationValueType, PodString<InternalComputationValueType>::Get() }, { keys::Dimensionality, std::to_string(D) } };
}
};
template< class InternalComputationValueType, int D >
struct InterfaceName< itkGaussianExponentialDiffeomorphicTransformParametersAdaptorsContainerInterface< InternalComputationValueType, D >>
{
static const char * Get()
{
return "itkGaussianExponentialDiffeomorphicTransformParametersAdaptorsContainerInterface";
}
};
// partial specialization of InterfaceName
// InterfaceName<T>::Get() should return the same name no matter whether T is an acceptor or provider interface.
template< typename InterfaceType >
struct InterfaceName< InterfaceAcceptor< InterfaceType >>
{
static const char * Get()
{
return InterfaceName< InterfaceType >::Get();
}
};
//************experimental**********
template< typename T >
struct Properties
struct Properties< itkGaussianExponentialDiffeomorphicTransformParametersAdaptorsContainerInterface< InternalComputationValueType, D >>
{
//static_assert(StaticErrorMessageRevealT<T>::False, "Please Implement InterfaceProperties<InterfaceType> for this InterfaceType");
static const std::map<std::string, std::string> Get()
{
return{ { "NameOfInterface", InterfaceName< T >::Get() } };
return { { keys::NameOfInterface, "itkGaussianExponentialDiffeomorphicTransformParametersAdaptorsContainerInterface" }, { keys::InternalComputationValueType, PodString<InternalComputationValueType>::Get() }, { keys::Dimensionality, std::to_string(D) } };
}
};
// The specializations for each type of Interface supported by the toolbox
template< int D, class TPixel >
struct Properties< itkImageFixedInterface< D, TPixel >>
{
static const std::map<std::string, std::string> Get()
{
return{ { "NameOfInterface", "itkImageFixedInterface" }, { "Dimensionality", std::to_string(D) }, { "PixelType", PodString<TPixel>::Get() } };
}
};
template< class C>
struct Properties< itkOptimizerv4Interface< C > >
{
static const std::map<std::string, std::string> Get()
{
return{ { "NameOfInterface", "itkOptimizerv4Interface" }, { "InternalComputationValueType", PodString<C>::Get() } };
}
};
} // end namespace selx
#endif // #define InterfaceTraits_h
......@@ -107,6 +107,9 @@ private:
/** Read configuration at the blueprints edges and try to find instantiated components */
void ApplyConnectionConfiguration();
/** For all uniquely selected components test handshake to non-uniquely selected components */
void PropagateConnectionsWithUniqueComponents();
/** See which components need more configuration criteria */
ComponentNamesType GetNonUniqueComponentNames();
......
......@@ -35,8 +35,12 @@ Overlord::Overlord() : m_isConfigured( false )
bool
Overlord::Configure()
{
//TODO: make a while loop until stable:
// - for each unique component propagate the required interfaces to neighboring components as added criterion
// Instantiates all the components as described in the blueprint. Returns true
// if all components could be uniquely selected.
// Configuration consists of 3 steps:
// - ApplyNodeConfiguration()
// - ApplyConnectionConfiguration()
// - PropagateConnectionsWithUniqueComponents();
if( !this->m_isConfigured )
{
......@@ -54,69 +58,7 @@ Overlord::Configure()
<< " Components could not be uniquely selected" << std::endl << std::endl;
std::cout << "===== Performing Handshakes between unique and non-unique Components =====" << std::endl;
bool anySelectionNarrowed(true);
while (anySelectionNarrowed){
anySelectionNarrowed = false;
nonUniqueComponentNames = this->GetNonUniqueComponentNames();
for (auto const & name : nonUniqueComponentNames)
{
// check all components that accept from component "name"
for (auto const & outgoingName : this->m_Blueprint->GetOutputNames(name))
{
// if the accepting component is also not uniquely selected, we do not try to check all valid combinations, since this would make the handshake logic too complicated
if (std::find(nonUniqueComponentNames.begin(), nonUniqueComponentNames.end(), outgoingName) == nonUniqueComponentNames.end())
{
Blueprint::ParameterMapType connectionProperties = this->m_Blueprint->GetConnection(name, outgoingName);
ComponentBase::InterfaceCriteriaType interfaceCriteria;
//TODO:
//1: this lambda function converts the blueprint properties: map<string,vector<string>> to interfacecriteria: map<string,string>, consider redesign.
//2: connection blueprint->addConnection("myfirstnode","mysecondnode",{{}}) creates connectionProperties {"",[]} which is not an empty map.
std::for_each(connectionProperties.begin(), connectionProperties.end(), [interfaceCriteria](Blueprint::ParameterMapType::value_type kv) mutable { if (kv.second.size() > 0) interfaceCriteria[kv.first] = kv.second[0]; });
auto outgoingComponent = this->m_ComponentSelectorContainer[outgoingName]->GetComponent();
const unsigned int beforeCriteria = this->m_ComponentSelectorContainer[name]->NumberOfComponents();
this->m_ComponentSelectorContainer[name]->RequireProvidingInterfaceTo(outgoingComponent, interfaceCriteria);
const unsigned int afterCriteria = this->m_ComponentSelectorContainer[name]->NumberOfComponents();
std::cout << afterCriteria << " " << name << " components can Provide to " << outgoingName << std::endl;
if (beforeCriteria > afterCriteria)
{
anySelectionNarrowed = true;
}
}
}
// check all components that provide to component "name"
for (auto const & incomingName : this->m_Blueprint->GetInputNames(name))
{
// if the providing component is also not uniquely selected, we do not try to check all valid combinations, since this would make the handshake logic too complicated
if (std::find(nonUniqueComponentNames.begin(), nonUniqueComponentNames.end(), incomingName) == nonUniqueComponentNames.end())
{
//std::cout << "Check which " << name << " component can Accept from " << incomingName << std::endl;
Blueprint::ParameterMapType connectionProperties = this->m_Blueprint->GetConnection(incomingName, name);
ComponentBase::InterfaceCriteriaType interfaceCriteria;
//TODO:
//1: this lambda function converts the blueprint properties: map<string,vector<string>> to interfacecriteria: map<string,string>, consider redesign.
//2: connection blueprint->addConnection("myfirstnode","mysecondnode",{{}}) creates connectionProperties {"",[]} which is not an empty map.
std::for_each(connectionProperties.begin(), connectionProperties.end(), [&interfaceCriteria](Blueprint::ParameterMapType::value_type kv) mutable { if (kv.second.size() > 0) interfaceCriteria[kv.first] = kv.second[0]; });
auto incomingComponent = this->m_ComponentSelectorContainer[incomingName]->GetComponent();
const unsigned int beforeCriteria = this->m_ComponentSelectorContainer[name]->NumberOfComponents();
this->m_ComponentSelectorContainer[name]->RequireAcceptingInterfaceFrom(incomingComponent, interfaceCriteria);
const unsigned int afterCriteria = this->m_ComponentSelectorContainer[name]->NumberOfComponents();
std::cout << afterCriteria << " " << name << " components can Accept from " << incomingName << std::endl;
if (beforeCriteria > afterCriteria)
{
anySelectionNarrowed = true;
}
}
}
}
}
this->PropagateConnectionsWithUniqueComponents();
this->m_isConfigured = true;
}
auto nonUniqueComponentNames = this->GetNonUniqueComponentNames();
......@@ -162,6 +104,10 @@ Overlord::GetNonUniqueComponentNames()
void
Overlord::ApplyNodeConfiguration()
{
// Creates a ComponentSelector for each node of the graph and apply
// the criteria/properties at each node to narrow the Component selection.
// At the overlord we store all components selectors in a mapping based
// on the keys we find in the graph. This is a flexible solution, but is
// fragile as well since correspondence is implicit.
......@@ -224,6 +170,15 @@ Overlord::ApplyNodeConfiguration()
void
Overlord::ApplyConnectionConfiguration()
{
// Read the criteria/properties at each Connection and narrow the selection of
// components.
// It does not necessarily mean that if connected Components fulfill the connection
// criteria they can be connected eventually. A criterion at a Connection could
// be e.g. that Dimensionality equals 3. The providing Component could have 1 (or more)
// interface that is of that dimensionality and the accepting interface as well, but the
// interfaces could still be of different types (including different other template arguments)
Blueprint::ComponentNamesType componentNames = this->m_Blueprint->GetComponentNames();
for( auto const & name : componentNames )
{
......@@ -266,6 +221,75 @@ Overlord::ApplyConnectionConfiguration()
}