/*========================================================================= * * Copyright NumFOCUS * * 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 * * https://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 itkExpectationMaximizationMixtureModelEstimator_hxx #define itkExpectationMaximizationMixtureModelEstimator_hxx #include "itkNumericTraits.h" #include "itkMath.h" namespace itk { namespace Statistics { template ExpectationMaximizationMixtureModelEstimator::ExpectationMaximizationMixtureModelEstimator() : m_Sample(nullptr) , m_MembershipFunctionsObject(MembershipFunctionVectorObjectType::New()) , m_MembershipFunctionsWeightArrayObject(MembershipFunctionsWeightsArrayObjectType::New()) {} template void ExpectationMaximizationMixtureModelEstimator::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Maximum Iteration: " << this->GetMaximumIteration() << std::endl; os << indent << "Sample: " << this->GetSample() << std::endl; os << indent << "Number Of Components: " << this->GetNumberOfComponents() << std::endl; for (unsigned int i = 0; i < this->GetNumberOfComponents(); ++i) { os << indent << "Component Membership Function[" << i << "]: " << this->GetComponentMembershipFunction(i) << std::endl; } os << indent << "Termination Code: " << this->GetTerminationCode() << std::endl; os << indent << "Initial Proportions: " << this->GetInitialProportions() << std::endl; os << indent << "Proportions: " << this->GetProportions() << std::endl; os << indent << "Calculated Expectation: " << this->CalculateExpectation() << std::endl; } template void ExpectationMaximizationMixtureModelEstimator::SetMaximumIteration(int numberOfIterations) { m_MaxIteration = numberOfIterations; } template int ExpectationMaximizationMixtureModelEstimator::GetMaximumIteration() const { return m_MaxIteration; } template void ExpectationMaximizationMixtureModelEstimator::SetInitialProportions(ProportionVectorType & proportions) { m_InitialProportions = proportions; } template auto ExpectationMaximizationMixtureModelEstimator::GetInitialProportions() const -> const ProportionVectorType & { return m_InitialProportions; } template auto ExpectationMaximizationMixtureModelEstimator::GetProportions() const -> const ProportionVectorType & { return m_Proportions; } template void ExpectationMaximizationMixtureModelEstimator::SetSample(const TSample * sample) { m_Sample = sample; } template const TSample * ExpectationMaximizationMixtureModelEstimator::GetSample() const { return m_Sample; } template int ExpectationMaximizationMixtureModelEstimator::AddComponent(ComponentType * component) { m_ComponentVector.push_back(component); return static_cast(m_ComponentVector.size()); } template unsigned int ExpectationMaximizationMixtureModelEstimator::GetNumberOfComponents() const { return static_cast(m_ComponentVector.size()); } template auto ExpectationMaximizationMixtureModelEstimator::GetTerminationCode() const -> TERMINATION_CODE_ENUM { return m_TerminationCode; } template auto ExpectationMaximizationMixtureModelEstimator::GetComponentMembershipFunction(int componentIndex) const -> ComponentMembershipFunctionType * { return (m_ComponentVector[componentIndex])->GetMembershipFunction(); } template bool ExpectationMaximizationMixtureModelEstimator::CalculateDensities() { bool componentModified = false; for (size_t i = 0; i < m_ComponentVector.size(); ++i) { if ((m_ComponentVector[i])->AreParametersModified()) { componentModified = true; break; } } if (!componentModified) { return false; } double temp; size_t numberOfComponents = m_ComponentVector.size(); std::vector tempWeights(numberOfComponents, 0.); typename TSample::ConstIterator iter = m_Sample->Begin(); typename TSample::ConstIterator last = m_Sample->End(); // Note: The data type of componentIndex should be unsigned int // because itk::Array only supports 'unsigned int' number of elements. unsigned int componentIndex; using FrequencyType = typename TSample::AbsoluteFrequencyType; FrequencyType frequency; FrequencyType zeroFrequency{}; typename TSample::MeasurementVectorType mvector; double density; double densitySum; double minDouble = NumericTraits::epsilon(); SizeValueType measurementVectorIndex = 0; while (iter != last) { mvector = iter.GetMeasurementVector(); frequency = iter.GetFrequency(); densitySum = 0.0; if (frequency > zeroFrequency) { for (componentIndex = 0; componentIndex < numberOfComponents; ++componentIndex) { double t_prop = m_Proportions[componentIndex]; double t_value = m_ComponentVector[componentIndex]->Evaluate(mvector); density = t_prop * t_value; tempWeights[componentIndex] = density; densitySum += density; } for (componentIndex = 0; componentIndex < numberOfComponents; ++componentIndex) { temp = tempWeights[static_cast(componentIndex)]; // just to make sure temp does not blow up! if (densitySum > NumericTraits::epsilon()) { temp /= densitySum; } m_ComponentVector[static_cast(componentIndex)]->SetWeight(measurementVectorIndex, temp); } } else { for (componentIndex = 0; componentIndex < numberOfComponents; ++componentIndex) { m_ComponentVector[componentIndex]->SetWeight(measurementVectorIndex, minDouble); } } ++iter; ++measurementVectorIndex; } return true; } template double ExpectationMaximizationMixtureModelEstimator::CalculateExpectation() const { double sum = 0.0; if (m_Sample) { unsigned int measurementVectorIndex; SizeValueType size = m_Sample->Size(); double logProportion; double temp; for (size_t componentIndex = 0; componentIndex < m_ComponentVector.size(); ++componentIndex) { temp = m_Proportions[static_cast(componentIndex)]; // if temp is below the smallest positive double number // the log may blow up if (temp > NumericTraits::epsilon()) { logProportion = std::log(temp); } else { logProportion = NumericTraits::NonpositiveMin(); } for (measurementVectorIndex = 0; measurementVectorIndex < size; ++measurementVectorIndex) { temp = m_ComponentVector[componentIndex]->GetWeight(measurementVectorIndex); if (temp > NumericTraits::epsilon()) { sum += temp * (logProportion + std::log(temp)); } else { // let's throw an exception itkExceptionMacro("temp is null"); } // m_ComponentVector[componentIndex]->GetWeight(measurementVectorIndex) ) ); } } } return sum; } template bool ExpectationMaximizationMixtureModelEstimator::UpdateComponentParameters() { bool updated = false; ComponentType * component; for (size_t componentIndex = 0; componentIndex < m_ComponentVector.size(); ++componentIndex) { component = m_ComponentVector[componentIndex]; component->Update(); if (component->AreParametersModified()) { updated = true; } } return updated; } template bool ExpectationMaximizationMixtureModelEstimator::UpdateProportions() { size_t numberOfComponents = m_ComponentVector.size(); size_t sampleSize = m_Sample->Size(); auto totalFrequency = static_cast(m_Sample->GetTotalFrequency()); size_t i, j; double tempSum; bool updated = false; for (i = 0; i < numberOfComponents; ++i) { tempSum = 0.; if (totalFrequency > NumericTraits::epsilon()) { for (j = 0; j < sampleSize; ++j) { tempSum += (m_ComponentVector[static_cast(i)]->GetWeight(static_cast(j)) * m_Sample->GetFrequency(static_cast(j))); } tempSum /= totalFrequency; } if (Math::NotAlmostEquals(tempSum, m_Proportions[static_cast(i)])) { m_Proportions[static_cast(i)] = tempSum; updated = true; } } return updated; } template void ExpectationMaximizationMixtureModelEstimator::GenerateData() { m_Proportions = m_InitialProportions; int iteration = 0; m_CurrentIteration = 0; while (iteration < m_MaxIteration) { m_CurrentIteration = iteration; if (this->CalculateDensities()) { this->UpdateComponentParameters(); this->UpdateProportions(); } else { m_TerminationCode = TERMINATION_CODE_ENUM::CONVERGED; break; } ++iteration; } m_TerminationCode = TERMINATION_CODE_ENUM::NOT_CONVERGED; } template auto ExpectationMaximizationMixtureModelEstimator::GetOutput() const -> const MembershipFunctionVectorObjectType * { size_t numberOfComponents = m_ComponentVector.size(); MembershipFunctionVectorType & membershipFunctionsVector = m_MembershipFunctionsObject->Get(); typename SampleType::MeasurementVectorSizeType measurementVectorSize = m_Sample->GetMeasurementVectorSize(); typename GaussianMembershipFunctionType::MeanVectorType mean; NumericTraits::SetLength(mean, measurementVectorSize); typename GaussianMembershipFunctionType::CovarianceMatrixType covariance; covariance.SetSize(measurementVectorSize, measurementVectorSize); typename ComponentType::ParametersType parameters; for (size_t i = 0; i < numberOfComponents; ++i) { parameters = m_ComponentVector[i]->GetFullParameters(); auto membershipFunction = GaussianMembershipFunctionType::New(); membershipFunction->SetMeasurementVectorSize(measurementVectorSize); unsigned int parameterIndex = 0; for (unsigned int j = 0; j < measurementVectorSize; ++j) { mean[j] = parameters[j]; ++parameterIndex; } for (unsigned int ii = 0; ii < measurementVectorSize; ++ii) { for (unsigned int jj = 0; jj < measurementVectorSize; ++jj) { covariance.GetVnlMatrix().put(ii, jj, parameters[parameterIndex]); ++parameterIndex; } } membershipFunction->SetMean(mean); membershipFunction->SetCovariance(covariance); membershipFunctionsVector.push_back(membershipFunction); } return static_cast(m_MembershipFunctionsObject); } template auto ExpectationMaximizationMixtureModelEstimator::GetMembershipFunctionsWeightsArray() const -> const MembershipFunctionsWeightsArrayObjectType * { size_t numberOfComponents = m_ComponentVector.size(); ProportionVectorType & membershipFunctionsWeightVector = m_MembershipFunctionsWeightArrayObject->Get(); membershipFunctionsWeightVector.SetSize(static_cast(numberOfComponents)); for (size_t i = 0; i < numberOfComponents; ++i) { membershipFunctionsWeightVector[static_cast(i)] = m_Proportions[static_cast(i)]; } return static_cast(m_MembershipFunctionsWeightArrayObject); } template void ExpectationMaximizationMixtureModelEstimator::Update() { this->GenerateData(); } } // end of namespace Statistics } // end of namespace itk #endif