/******************************************************************************* * Copyright 2015 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.matlabproxy; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import org.eclipse.jdt.annotation.Nullable; import com.analog.lyric.dimple.environment.DimpleEnvironment; import com.analog.lyric.dimple.exceptions.DimpleException; import com.analog.lyric.dimple.model.core.INode; import com.analog.lyric.dimple.model.core.Node; import com.analog.lyric.dimple.options.DimpleOptionRegistry; import com.analog.lyric.dimple.schedulers.CustomScheduler; import com.analog.lyric.dimple.schedulers.IGibbsScheduler; import com.analog.lyric.dimple.schedulers.IScheduler; import com.analog.lyric.dimple.schedulers.SchedulerOptionKey; import com.analog.lyric.dimple.schedulers.schedule.ScheduleValidationException; import com.analog.lyric.dimple.schedulers.scheduleEntry.IBlockUpdater; import com.analog.lyric.options.IOptionKey; import com.analog.lyric.options.OptionKey; import com.analog.lyric.util.misc.Matlab; /** * MATLAB proxy for {@link IScheduler} * @since 0.08 * @author Christopher Barber */ @Matlab(wrapper="Scheduler") public class PScheduler extends PObject { /*------- * State */ private final IScheduler _scheduler; /*-------------- * Construction */ PScheduler(IScheduler scheduler) { _scheduler = scheduler; } /** * Constructs a custom scheduler with specified entries. * <p> * @param pgraph identifies the graph for which the scheduler is being created * @since 0.08 */ PScheduler(PFactorGraphVector pgraph, SchedulerOptionKey schedulerKey, @Nullable Object[] scheduleEntries) { this(new CustomScheduler(pgraph.getGraph(), schedulerKey)); addCustomEntries(scheduleEntries); } /** * @deprecated only to support Matlab FactorGraph.Schedule setter */ @Deprecated PScheduler(PFactorGraphVector pgraph, @Nullable Object[] scheduleEntries) { this(new CustomScheduler(pgraph.getGraph())); addCustomEntries(scheduleEntries); } PScheduler(PFactorGraphVector pgraph, String schedulerKey, @Nullable Object[] scheduleEntries) { this(pgraph, lookupSchedulerKey(pgraph.getGraph().getEnvironment(), schedulerKey), scheduleEntries); } private static SchedulerOptionKey lookupSchedulerKey(DimpleEnvironment env, String schedulerKey) { final DimpleOptionRegistry options = env.optionRegistry(); IOptionKey<?> key = null; if (schedulerKey.contains(".")) { // If str contains a dot require an exact match. key = options.get(schedulerKey); } else { // Otherwise use a regexp ArrayList<IOptionKey<?>> keys = options.getAllMatching(Pattern.quote(schedulerKey) + "\\w+\\.scheduler"); switch (keys.size()) { case 0: break; case 1: key = keys.get(0); break; default: throw new ScheduleValidationException("'%s' is ambiguous could be any of: %s", schedulerKey, keys); } } if (key == null) { throw new ScheduleValidationException("'%s' does not refer to a known option key", schedulerKey); } if (!(key instanceof SchedulerOptionKey)) { throw new ScheduleValidationException("'%s' does not refer to a scheduler option key", schedulerKey); } return (SchedulerOptionKey)key; } /*----------------- * PObject methods */ @Override public IScheduler getDelegate() { return _scheduler; } @Override public IScheduler getModelerObject() { return _scheduler; } @Override public boolean isScheduler() { return true; } /*-------------------- * PScheduler methods */ public void addBlockScheduleEntry(IBlockUpdater blockUpdater, PVariableBlock block) { if (_scheduler instanceof IGibbsScheduler) { IGibbsScheduler scheduler = (IGibbsScheduler)_scheduler; scheduler.addBlockWithReplacement(blockUpdater, block.getDelegate()); } else { throw new UnsupportedOperationException("addBlockScheduleEntry cannot be used on non-Gibbs scheduler"); } } public void addBlockScheduleEntry(IBlockUpdater blockUpdater, Object[] variables) { addBlockScheduleEntry(blockUpdater, PFactorGraphVector.addVariableBlock(null, variables)); } /** * Add entries to a custom schedule. * <p> * @param scheduleEntries an ordered list of the following types of entries: * <ul> * <li>{@link SchedulerOptionKey} instance or its {@linkplain OptionKey#qualifiedName() qualified name} * may be specified as the first entry in the list to identify the scheduler type. * <li>{@link PNodeVector} containing node entries * <li>An {@code Object[]} array containing two elements specifying an edge schedule entry. The first * element is a single-node {@link PNodeVector} specifying the originating node, and the second entry * specifying the destination node for the edge update. If there is more than one edge connecting the * source and destination, this specifies the lowest numbered one with respect to the origin node. * <li>A n {@code Object[]} array containing a block schedule entry specification. The first element * is an instance of {@link IBlockUpdater} and the remaining entries should contain the variables in * the block. * </ul> * @throws UnsupportedOperationException if this does not hold a {@link CustomScheduler}. * @since 0.08 */ public void addCustomEntries(@Nullable Object[] scheduleEntries) { final CustomScheduler scheduler = assertCustom(); if (scheduleEntries == null) { return; } //Convert schedule to a list of nodes and edges for (Object entry : scheduleEntries) { if (entry instanceof Object[]) { final Object[] array = (Object[])entry; if (array.length >= 2 && array[0] instanceof IBlockUpdater) { // This is a block schedule entry final IBlockUpdater blockUpdater = (IBlockUpdater)array[0]; if (array[1] instanceof PVariableBlock) { scheduler.addBlock(blockUpdater, ((PVariableBlock)array[1]).getDelegate()); } else { scheduler.addBlock(blockUpdater, PHelpers.convertToVariableArray(array, 1)); } } else { // Entry is a pair of nodes, that represent an edge if (array.length != 2) throw new DimpleException("Length of array containing edge must be 2"); INode node1 = PHelpers.convertToNode(array[0]); INode node2 = PHelpers.convertToNode(array[1]); int portNum = node1.findSibling(node2); scheduler.addEdge(node1, portNum); } } else { for (Node node : PHelpers.convertToNodeArray(entry)) { scheduler.addNode(node); } } } } public void addCustomPath(Object[] nodes) { ArrayList<INode> list = new ArrayList<>(nodes.length); for (Object obj : nodes) { if (obj instanceof INode) { list.add((INode)obj); } else if (obj instanceof PNodeVector) { PNodeVector vec = (PNodeVector)obj; if (vec.size() != 1) { throw new IllegalArgumentException("Can only use scalar nodes in path."); } list.add(vec.getModelerNode(0)); } } assertCustom().addPath(list); } public String[] applicableSchedulerOptions() { final List<? extends SchedulerOptionKey> keys = _scheduler.applicableSchedulerOptions(); final int n = keys.size(); final String[] names = new String[n]; for (int i = 0; i < n; ++i) { names[i] = keys.get(i).qualifiedName(); } return names; } /*----------------- * Private methods */ private CustomScheduler assertCustom() { if (!(_scheduler instanceof CustomScheduler)) { throw new UnsupportedOperationException(String.format("%s is not a CustomScheduler", _scheduler)); } return (CustomScheduler)_scheduler; } }