/*
* Created on Oct 25, 2006 Copyright (C) 2001-6, Anthony Harrison anh23@pitt.edu
* (jactr.org) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version. This library is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU Lesser General Public License for more details. You should have
* received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.jactr.core.module.declarative.four.associative;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
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.chunk.IChunk;
import org.jactr.core.chunk.four.ISubsymbolicChunk4;
import org.jactr.core.chunk.four.Link4;
import org.jactr.core.module.declarative.four.learning.IDeclarativeLearningModule4;
import org.jactr.core.module.procedural.event.ProceduralModuleEvent;
import org.jactr.core.module.procedural.event.ProceduralModuleListenerAdaptor;
import org.jactr.core.production.IInstantiation;
import org.jactr.core.production.condition.IBufferCondition;
import org.jactr.core.production.condition.ICondition;
import org.jactr.core.slot.ISlot;
/**
* we use a proceduralmodule listener to track the chunks that are accessed in
* service of firing a production. We listen to two methods:
* productionWillFire() to snag all the chunks that the production requires to
* fire, and productionFired() to call the various parameter update methods on
* the chunks
*
* @author developer
*/
public class ProceduralModuleListener extends ProceduralModuleListenerAdaptor
{
/**
* logger definition
*/
static private final Log LOGGER = LogFactory
.getLog(ProceduralModuleListener.class);
static private final String BOUND_GOAL = "="
+ IActivationBuffer.GOAL;
/*
* all the chunks that a production matches against, keyed on buffer name
*/
private final SortedMap<String, IChunk> _matchedChunks;
private final IDeclarativeLearningModule4 _learningModule;
public ProceduralModuleListener(IDeclarativeLearningModule4 learning)
{
_matchedChunks = new TreeMap<String, IChunk>();
_learningModule = learning;
}
/**
* snag the chunks that the production that is about to fire is matched
* against
*
* @see org.jactr.core.module.procedural.event.IProceduralModuleListener#productionWillFire(org.jactr.core.module.procedural.event.ProceduralModuleEvent)
*/
@Override
public void productionWillFire(ProceduralModuleEvent pme)
{
if (!_learningModule.isAssociativeLearningEnabled()) return;
/*
* snag all the chunks that will be matched against
*/
_matchedChunks.clear();
IInstantiation instantiation = (IInstantiation) pme.getProduction();
for (ICondition condition : instantiation.getSymbolicProduction()
.getConditions())
if (condition instanceof IBufferCondition)
{
IBufferCondition bufferCondition = (IBufferCondition) condition;
Object boundVariable = instantiation.getVariableBindings().get(
"=" + bufferCondition.getBufferName());
if (boundVariable instanceof IChunk)
_matchedChunks.put(bufferCondition.getBufferName(),
(IChunk) boundVariable);
}
}
/**
* adjust the parameter values for the chunks that enabled this production to
* fire
*
* @see org.jactr.core.module.procedural.event.IProceduralModuleListener#productionFired(org.jactr.core.module.procedural.event.ProceduralModuleEvent)
*/
@Override
public void productionFired(ProceduralModuleEvent pme)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("Adjusting chunk parameter values");
if (_learningModule.isAssociativeLearningEnabled())
updateStatistics(_matchedChunks);
}
public void updateStatistics(SortedMap<String, IChunk> matchedChunks)
{
FastList<ISlot> slots = FastList.newInstance();
for (Map.Entry<String, IChunk> matchedEntry : matchedChunks.entrySet())
{
IChunk matchedChunk = matchedEntry.getValue();
/*
* all chunks that were matched were needed..
*/
ISubsymbolicChunk4 sscMatched = matchedChunk
.getAdapter(ISubsymbolicChunk4.class);
sscMatched.incrementTimesNeeded(1);
/**
* and in 4.0 the goal defines the context
*/
if (matchedEntry.getKey().equalsIgnoreCase(BOUND_GOAL))
{
/*
* the contents of the chunk define the context. Ideally we'd only use
* those slots that were matched in the production. but for now, we'll
* use everybody
*/
slots.clear();
for (ISlot slot : matchedChunk.getSymbolicChunk().getSlots(slots))
if (slot.getValue() instanceof IChunk)
{
IChunk jChunk = (IChunk) slot.getValue();
ISubsymbolicChunk4 sscJ = jChunk
.getAdapter(ISubsymbolicChunk4.class);
sscJ.incrementTimesInContext(1);
/*
* if J is in the context and there is a link between J and another
* matched Chunk (i), then we need to strengthen that association
* directly.
*/
for (Map.Entry<String, IChunk> iEntry : matchedChunks.tailMap(
matchedEntry.getKey()).entrySet())
{
IChunk iChunk = iEntry.getValue();
// skip ourselves
if (iChunk.equals(matchedChunk)) continue;
// Link4 link = (Link4) sscJ.getIAssociation(iChunk);
Link4 link = (Link4) ChunkListener.getAssociativeLink(jChunk,
iChunk, true);
/*
* the link exists. It might not.. should we create one here?
*/
if (link != null)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug(String.format("Incrementing F(Ni|Cj) for %s",
link));
link.incrementFNICJ();
}
}
}
}
}
FastList.recycle(slots);
}
}