package org.jactr.core.buffer.six; /* * default logging */ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javolution.util.FastList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.buffer.IActivationBuffer; import org.jactr.core.buffer.ISourceActivationSpreader; import org.jactr.core.chunk.IChunk; import org.jactr.core.chunk.ISubsymbolicChunk; import org.jactr.core.chunk.four.ISubsymbolicChunk4; import org.jactr.core.chunk.four.Link4; import org.jactr.core.chunk.link.IAssociativeLink; import org.jactr.core.logging.IMessageBuilder; import org.jactr.core.logging.Logger; import org.jactr.core.logging.Logger.Stream; import org.jactr.core.model.IModel; /** * default activation spreader. this is not thread safe and assumes that * activation calculations are done serially. * * @author harrison */ public class DefaultSourceActivationSpreader implements ISourceActivationSpreader { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(DefaultSourceActivationSpreader.class); private final IActivationBuffer _buffer; private final Map<IChunk, Integer> _activatedChunks; private double _activationPortion; public DefaultSourceActivationSpreader(IActivationBuffer buffer) { _buffer = buffer; _activatedChunks = new HashMap<IChunk, Integer>(); } public Set<IChunk> getActivatedChunks(Set<IChunk> container) { if (container == null) container = new HashSet<IChunk>(); container.addAll(_activatedChunks.keySet()); return container; } public IActivationBuffer getBuffer() { return _buffer; } /** * divies source activation amoung the chunks linked to the source chunks. */ public void spreadSourceActivation() { clearSourceActivation(); // nothing to spread if (_buffer.getActivation() == 0) return; /* * we use an array list which WILL permit duplicates. Why? shouldn't * something appearing N times in a source chunk get N times the source * activation? Note: this is not the same as the count used in the links. * Count & links deal with the spreading activation, this is source * activation. SO, if a source chunk has two slot references to X, it will * also have a link from X with a count of 2. X will receive twice the * source activation, and will propogate that through the doubled link. */ FastList<IAssociativeLink> jLinks = FastList.newInstance(); FastList<IChunk> sourceChunks = FastList.newInstance(); try { _buffer.getSourceChunks(sourceChunks); if (sourceChunks.size() == 0) return; if (LOGGER.isDebugEnabled()) LOGGER .debug(String .format( "Calculating source activation to propogate through %s to %s @ %.2f", _buffer.getName(), sourceChunks, _buffer.getModel() .getAge())); /* * we need to get each chunk that the source contains. We could use the * slots and iterate through those that have chunks as values, but that * wouldn't be very flexible in terms of a long term associative * perspective. instead we look at the jLinks (links where the source * chunk is the iChunk), which will include the slot value and other * associated chunks. We ignore the strengths, and just use them to * determine the sources of activation */ int numLinks = 0; for (IChunk sourceChunk : sourceChunks) { ISubsymbolicChunk4 ssc4 = sourceChunk .getAdapter(ISubsymbolicChunk4.class); if (ssc4 != null) { jLinks.clear(); ssc4.getJAssociations(jLinks); for (IAssociativeLink link : jLinks) { IChunk jChunk = link.getJChunk(); if (jChunk.hasBeenDisposed()) continue; /* * we might be tempted to check for a self link to exclude, but no. * We need to include it. While this is propogating source * activation, taht is not part of the total activation equation * (just spread). Only by propogating source to the self does spread * reach the slot values of this chunk. */ int count = 1; if (link instanceof Link4) count = ((Link4) link).getCount(); // we need to check activated chunks because the source chunks // might all point to similar chunks if (_activatedChunks.containsKey(jChunk)) count += _activatedChunks.get(jChunk); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s(i) - %s(j) has %d links", sourceChunk, jChunk, count)); numLinks += count; _activatedChunks.put(jChunk, count); } } } /* * now we zip through the links */ if (numLinks != 0) { _activationPortion = _buffer.getActivation() / numLinks; IModel model = getBuffer().getModel(); IMessageBuilder logMsg = null; if (Logger.hasLoggers(model)) { logMsg = Logger.messageBuilder(); logMsg.append(getBuffer().getName()).append(" spreading ") .append(String.format("%.2f", _activationPortion)); logMsg.append(" to each "); } if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format( "Activating associated chunks %s with %.2f", _activatedChunks, _activationPortion)); for (Map.Entry<IChunk, Integer> entry : _activatedChunks.entrySet()) { IChunk chunk = entry.getKey(); double source = _activationPortion * entry.getValue(); ISubsymbolicChunk ssc = chunk.getSubsymbolicChunk(); ssc.setSourceActivation(_buffer, source); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s has %.2f", entry.getKey(), ssc.getSourceActivation())); if (logMsg != null) logMsg.append(chunk.getSymbolicChunk().getName()).append(" "); } if (logMsg != null) Logger.log(model, Stream.ACTIVATION, logMsg); } else if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("No associated chunks to activate")); } finally { FastList.recycle(jLinks); FastList.recycle(sourceChunks); } } public void clearSourceActivation() { if (_activatedChunks.size() == 0) return; if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format( "Clearing source activation (%.2f) propogated from %s to %s @ %.2f", _activationPortion, _buffer.getName(), _activatedChunks, _buffer .getModel().getAge())); for (IChunk chunk : _activatedChunks.keySet()) { // a chunk may have been disposed of by now... if (chunk.hasBeenDisposed()) continue; ISubsymbolicChunk ssc = chunk.getSubsymbolicChunk(); // double activation = ssc.getSourceActivation() - _activationPortion; ssc.setSourceActivation(_buffer, 0); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s has %.2f", chunk, ssc.getSourceActivation())); } _activatedChunks.clear(); _activationPortion = 0; } }