/******************************************************************************* * Copyright 2014 Analog Devices, Inc. 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 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. ********************************************************************************/ package com.analog.lyric.dimple.solvers.optimizedupdate; import java.util.ArrayList; import java.util.List; import com.analog.lyric.dimple.factorfunctions.core.IFactorTable; import com.analog.lyric.math.Utilities; import com.analog.lyric.util.misc.Internal; /** * A plan for updating a factor. Each factor table has a unique plan, but factors that share a * factor table share a plan. A plan consists of a list of steps to perform to an actual factor, * which provides the actual edge messages and other per-factor properties. * * @since 0.06 * @author jking */ @Internal public final class FactorUpdatePlan { private final List<IUpdateStep> _steps; private FactorUpdatePlan(List<IUpdateStep> steps) { _steps = steps; } /** * Constructs an update plan for a given factor table. The same plan may be applied to any * factor that uses the same factor table. * * @since 0.06 */ public static FactorUpdatePlan create(final IFactorTable factorTable, final ISFactorGraphToOptimizedUpdateAdapter tableWrapperAdapter, double sparseThreshold) { final int n = factorTable.getDimensions(); // Initial capacity is just an estimate. final List<IUpdateStep> steps = new ArrayList<IUpdateStep>((int) (n * Utilities.log2(n))); ITreeBuilder<TableWrapper> builder = new TreeBuilder(steps, tableWrapperAdapter, sparseThreshold); TreeWalker<TableWrapper> treeWalker = new TreeWalker<TableWrapper>(factorTable); treeWalker.accept(builder); return new FactorUpdatePlan(steps); } private static class TreeBuilder implements ITreeBuilder<TableWrapper> { private final List<IUpdateStep> _steps; private final ISFactorGraphToOptimizedUpdateAdapter _tableWrapperHelper; private final double _sparseThreshold; public TreeBuilder(List<IUpdateStep> steps, final ISFactorGraphToOptimizedUpdateAdapter tableWrapperAdapter, double sparseThreshold) { _steps = steps; _tableWrapperHelper = tableWrapperAdapter; _sparseThreshold = sparseThreshold; } @Override public TableWrapper createRootT(IFactorTable rootFactorTable) { return new TableWrapper(rootFactorTable, _tableWrapperHelper, _sparseThreshold); } @Override public TableWrapper buildMarginalize(TableWrapper g, int portNum, int localDimension) { final IMarginalizationStep marginalizeStep = g.createMarginalizationStep(portNum, localDimension); _steps.add(marginalizeStep); return marginalizeStep.getAuxiliaryTable(); } @Override public void buildOutput(TableWrapper g, int portNum) { IUpdateStep outputStep = g.createOutputStep(portNum); _steps.add(outputStep); } } /** * Applies an update plan to a factor. The factor must use the same factor table as used to * construct the plan. * * @since 0.06 */ public void apply(ISTableFactorSupportingOptimizedUpdate tableFactor) { for (final IUpdateStep step : _steps) { step.apply(tableFactor); } } /** * Estimates the costs of applying the optimized factor update algorithm to a factor. * <p> * The estimation assumes that the content of sparse tables is distributed randomly throughout * the table, rather than examining the actual locations of the data. Using this assumption, * the density of each auxiliary table is computed without actually populating their data. * * @param factorTable A factor table that describes the factor. * @return The estimated costs. * @since 0.07 */ public static Costs estimateOptimizedUpdateCosts(IFactorTable factorTable, final ISFactorGraphToOptimizedUpdateAdapter tableWrapperHelper, final double sparseThrehsold) { final Costs costs = new Costs(); ITreeBuilder<CostEstimationTableWrapper> builder = new EstimationTreeBuilder(costs, tableWrapperHelper, sparseThrehsold); TreeWalker<CostEstimationTableWrapper> treeWalker = new TreeWalker<CostEstimationTableWrapper>(factorTable); treeWalker.accept(builder); return costs; } private static class EstimationTreeBuilder implements ITreeBuilder<CostEstimationTableWrapper> { private final ISFactorGraphToOptimizedUpdateAdapter _tableWrapperHelper; private final Costs _result; private final double _sparseThreshold; public EstimationTreeBuilder(Costs result, ISFactorGraphToOptimizedUpdateAdapter tableWrapperHelper, double sparseThreshold) { _result = result; _tableWrapperHelper = tableWrapperHelper; _sparseThreshold = sparseThreshold; } @Override public CostEstimationTableWrapper createRootT(IFactorTable rootFactorTable) { return new CostEstimationTableWrapper(rootFactorTable, _tableWrapperHelper, _sparseThreshold); } @Override public CostEstimationTableWrapper buildMarginalize(CostEstimationTableWrapper g, int portNum, int localDimension) { final IMarginalizationStepEstimator marginalizeStep = g.createMarginalizationStep(portNum, localDimension); _result.add(marginalizeStep.estimateCosts()); return marginalizeStep.getAuxiliaryTable(); } @Override public void buildOutput(CostEstimationTableWrapper g, int portNum) { IUpdateStepEstimator outputStep = g.createOutputStep(portNum); _result.add(outputStep.estimateCosts()); } } }