package org.jactr.core.module.declarative.basic; /* * default logging */ import java.util.ArrayList; import java.util.Collection; import javolution.util.FastList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.chunk.IChunk; import org.jactr.core.chunk.event.IChunkListener; import org.jactr.core.chunk.four.ISubsymbolicChunk4; import org.jactr.core.chunk.four.Link4; import org.jactr.core.chunk.link.DefaultAssociativeLinkEquation; import org.jactr.core.chunk.link.IAssociativeLink; import org.jactr.core.chunk.link.IAssociativeLinkEquation; import org.jactr.core.model.IModel; import org.jactr.core.module.declarative.associative.IAssociativeLinkContainer; import org.jactr.core.module.declarative.associative.IAssociativeLinkageSystem; import org.jactr.core.module.declarative.four.learning.DeclarativeModuleListener; import org.jactr.core.utils.parameter.ACTRParameterProcessor; import org.jactr.core.utils.parameter.LinkParameterHandler; import org.jactr.core.utils.parameter.LinkParameterProcessor; /** * creates {@link Link4} links, but does not install any code to * add/remove/learn the links * * @author harrison */ public class DefaultAssociativeLinkageSystem implements IAssociativeLinkageSystem { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(DefaultAssociativeLinkageSystem.class); private IAssociativeLinkEquation _equation; public DefaultAssociativeLinkageSystem() { setAssociativeLinkEquation(new DefaultAssociativeLinkEquation()); } /** * creates the default link. If you want to use a different type of Link, * override this first. */ public IAssociativeLink createLink(IChunk iChunk, IChunk jChunk) { Link4 link = new Link4(jChunk, iChunk); link.setAssociativeLinkEquation(_equation); return link; } public IAssociativeLinkEquation getAssociativeLinkEquation() { return _equation; } public void setAssociativeLinkEquation(IAssociativeLinkEquation equation) { _equation = equation; } public void install(IModel model) { } public void uninstall(IModel model) { } /** * associative linkers will often require chunk listeners to perform their * job. This null interface is provided so that hooks can be more generally * written to attach the linker's listener to chunks that are created. See * {@link DeclarativeModuleListener} * * @return */ public IChunkListener getChunkListener() { return null; } public LinkParameterHandler getParameterHandler() { return new LinkParameterHandler(); } public LinkParameterProcessor getParameterProcessor(final IChunk sourceChunk) { return new LinkParameterProcessor(ISubsymbolicChunk4.LINKS, l -> { addLink(l); }, () -> { IAssociativeLinkContainer container = sourceChunk .getAdapter(IAssociativeLinkContainer.class); Collection<IAssociativeLink> lC = new ArrayList<IAssociativeLink>(); container.getOutboundLinks(sourceChunk, lC); if (lC.size() > 0) return lC.iterator().next(); else return null; }, new ACTRParameterProcessor("bsName", null, null, sourceChunk.getModel()), sourceChunk); } /** * here we clean up the associative links. This is called during disposal of a * chunk, which can occur for temporary (during runtime), as well as encoded * chunks (usually after runtime) */ public void chunkWillBeDisposed(IChunk chunk) { IAssociativeLinkContainer container = chunk .getAdapter(IAssociativeLinkContainer.class); // remove the associative links. FastList<IAssociativeLink> links = FastList.newInstance(); // where chunk is J container.getOutboundLinks(links); for (IAssociativeLink link : links) detachLink(link); // where chunk is I links.clear(); container.getInboundLinks(links); for (IAssociativeLink link : links) detachLink(link); FastList.recycle(links); } protected void detachLink(IAssociativeLink link) { IChunk iChunk = link.getIChunk(); IChunk jChunk = link.getJChunk(); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("Detaching link %s", link)); if (!iChunk.hasBeenDisposed()) iChunk.getAdapter(IAssociativeLinkContainer.class).removeLink(link); if (!jChunk.hasBeenDisposed()) jChunk.getAdapter(IAssociativeLinkContainer.class).removeLink(link); } /* * copy links from source, remapping all instances of source to destination, * then add the links to destination (and associated chunks) (non-Javadoc) * @see * org.jactr.core.module.declarative.associative.IAssociativeLinkageSystem * #copyAndRemapLinks(org.jactr.core.chunk.IChunk, * org.jactr.core.chunk.IChunk) */ public void copyAndRemapLinks(IChunk source, IChunk destination, boolean copyInboundLinks, boolean copyOutboundLinks) { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("Copying and remapping links from %s to %s", source, destination)); // zip through all of sources links IAssociativeLinkContainer srcCont = source .getAdapter(IAssociativeLinkContainer.class); IAssociativeLinkContainer destCont = destination .getAdapter(IAssociativeLinkContainer.class); if (srcCont != null && destCont != null) { // remove the associative links. FastList<IAssociativeLink> links = FastList.newInstance(); // j links, that is these links spread activation from source if (copyOutboundLinks) { srcCont.getOutboundLinks(links); if (LOGGER.isDebugEnabled()) LOGGER .debug(String .format( "Copying and remapping %d outbound links (those that spread activation from destination)", links.size())); for (IAssociativeLink link : links) remapAndInstall(source, destination, link); } if (copyInboundLinks) { links.clear(); // i links, that is, these links spread activation to source srcCont.getInboundLinks(links); if (LOGGER.isDebugEnabled()) LOGGER .debug(String .format( "Copying and remapping %d inbound links (those that spread activation into destination)", links.size())); for (IAssociativeLink link : links) remapAndInstall(source, destination, link); } FastList.recycle(links); } else if (LOGGER.isWarnEnabled()) LOGGER.warn(String .format("Both source and destination must be ISubsymbolicChunk4")); } protected void remapAndInstall(IChunk source, IChunk dest, IAssociativeLink link) { // source can be i,j or both (for self-links) boolean sourceIsI = link.getIChunk().equals(source); boolean sourceIsJ = link.getJChunk().equals(source); /* * we need to see if the link has already been created in dest.This can * happen for the slot values of dest (already copied) - which will * automatically have links to the slot values (depending on the containment * linking policy) */ Link4 newLink = (Link4) getOrCreateLink(dest, link, sourceIsI, sourceIsJ); // Link4 newLink = (Link4) createLink(sourceIsI ? dest : link.getIChunk(), // sourceIsJ ? dest : link.getJChunk()); Link4 oldLink = (Link4) link; newLink.setCount(oldLink.getCount()); newLink.setFNICJ(oldLink.getFNICJ()); newLink.setRStrength(oldLink.getRStrength()); newLink.setStrength(oldLink.getStrength()); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("Remapped link from %s (0x%d) to %s (0x%d)", oldLink, oldLink.hashCode(), newLink, newLink.hashCode())); } protected IAssociativeLink getOrCreateLink(IChunk destinationChunk, IAssociativeLink link, boolean destIsI, boolean destIsJ) { // may need to be ISubsymbolicChunk4 for older code IAssociativeLinkContainer destContainer = destinationChunk .getAdapter(IAssociativeLinkContainer.class); FastList<IAssociativeLink> container = FastList.newInstance(); IChunk other = null; try { if (destIsI) { other = link.getJChunk(); // ISubsymbolicChunk4.getJAssociation(other) destContainer.getInboundLinks(other, container); } else { other = link.getIChunk(); // ISubsymbolicChunk4.getIAssociation(other) destContainer.getOutboundLinks(other, container); } /* * the link already exists */ if (container.size() > 0) { if (container.size() == 1) { if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format( "%s already has a link to %s, returning %s", destinationChunk, other, link)); } else if (LOGGER.isWarnEnabled()) LOGGER.warn(String.format( "Multiple links between %s and %s found, returning first", destinationChunk, other)); return container.iterator().next(); } else { /* new link */ IAssociativeLink newLink = createLink(destIsI ? destinationChunk : other, destIsJ ? destinationChunk : other); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format( "No existing link between %s and %s, creating and adding %s", destinationChunk, other, newLink)); /* * add the new link to the other chunk, but only if this isn't naturally * a self-link. (that should be caught above) */ // dest.getSubsymbolicChunk().addLink destContainer.addLink(newLink); if (!(destIsI && destIsJ)) { // other.getSubsymbolicChunk().addLink other.getAdapter(IAssociativeLinkContainer.class).addLink(newLink); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("Added %s to %s", newLink, other)); } return newLink; } } finally { FastList.recycle(container); } } public void addLink(IAssociativeLink link) { IChunk sourceJ = link.getJChunk(); IChunk targetI = link.getIChunk(); /** * add the link to both, unless they are the same */ sourceJ.getAdapter(IAssociativeLinkContainer.class).addLink(link); if (sourceJ != targetI) targetI.getAdapter(IAssociativeLinkContainer.class).addLink(link); } public void removeLink(IAssociativeLink link) { IChunk sourceJ = link.getJChunk(); IChunk targetI = link.getIChunk(); sourceJ.getAdapter(IAssociativeLinkContainer.class).removeLink(link); targetI.getAdapter(IAssociativeLinkContainer.class).removeLink(link); } }