/**
* Copyright (C) 2001-3, Anthony Harrison anh23@pitt.edu 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.chunk.four;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.chunk.IChunk;
import org.jactr.core.chunk.ISubsymbolicChunk;
import org.jactr.core.model.IModel;
import org.jactr.core.module.declarative.four.learning.IDeclarativeLearningModule4;
import org.jactr.core.runtime.ACTRRuntime;
/**
* A Link represents a subsymbolic associative link between two chunks J and I.
* Spreading activation propogates from J to I. There are two ways to create a
* Link: 1) From Chunkj to Chunki when one of Chunkj's slots contains Chunki 2)
* From Chunkj to Chunki when Chunkj is in the goal buffer and Chunki has been
* positively matched within any other buffer
*
* @note : this is a point of divergence from ACT-R 5.0. 5.0's associative link
* contract is less than specific. In 4.0 condition 2) is different in
* that it would only be true for the retrieval buffer. Additionally, 5.0
* is attempting to move to an entirely similarity based system. We are
* still waiting for final word as to how this will be handled.
* @author harrison
* @created April 18, 2003
*/
public class Link
{
private static transient Log LOGGER = LogFactory.getLog(Link.class
.getName());
/**
* Description of the Field
*/
public IChunk _jChunk;
// j
/**
* Description of the Field
*/
public IChunk _iChunk;
// i
/**
* Description of the Field
*/
public int _count;
// Number of times this link is used
/**
* Description of the Field
*/
public double _rStrength = 1;
/**
* Description of the Field
*/
public double _strength;
/**
* Description of the Field
*/
public double _fnicj;
/**
* Description of the Field
*/
public double _timeStamp;
/**
* The j chunk should contain the I chunk as a slot value.
*
* @param j
* Description of the Parameter
* @param i
* Description of the Parameter
*/
public Link(IChunk j, IChunk i)
{
this(j, i, 1, Double.NaN);
}
/**
* Constructor for the Link object
*
* @param j
* Description of the Parameter
* @param i
* Description of the Parameter
* @param count
* Description of the Parameter
* @param strength
* Description of the Parameter
*/
public Link(IChunk j, IChunk i, int count, double strength)
{
_jChunk = j;
_iChunk = i;
_count = count;
if (!Double.isNaN(strength))
setStrength(strength);
else
resetStrength();
}
/**
* used for copying the values from an existing link when a copy of j is made
*
* @param j
* @param i
* @param link
*/
public Link(IChunk newJ, Link link)
{
_jChunk = newJ;
_iChunk = link._iChunk;
_count = link._count;
_fnicj = link._fnicj;
_rStrength = link._rStrength;
_strength = link._strength;
_timeStamp = link._timeStamp;
}
/**
* Return the containing chunk
*
* @return The jChunk value
*/
public IChunk getJChunk()
{
return _jChunk;
}
/**
* Return the contained chunk
*
* @return The iChunk value
*/
public IChunk getIChunk()
{
return _iChunk;
}
/**
* A link can actually represent multiple links between two chunks. This value
* represents the number of links this one actually is.
*
* @return The count value
*/
public int getCount()
{
return _count;
}
/**
* Gets the fNICJ attribute of the Link object
*
* @return The fNICJ value
*/
public double getFNICJ()
{
return _fnicj;
}
/**
* Description of the Method
*/
public void incrementFNICJ()
{
_fnicj += 1.0;
dirty();
}
/**
* Returns the R strength which is the prelog transformed strength value
*
* @return The rStrength value
*/
public double getRStrength()
{
if (isDirty()) computeStrength();
return _rStrength;
}
/**
* return the log transformed strength.
*
* @return The strength value
*/
public double getStrength()
{
if (isDirty()) computeStrength();
return _strength;
}
/**
* Description of the Method
*
* @return Description of the Return Value
*/
@Override
public String toString()
{
return String.format("%s %f %s (%d, %f, %f)", _jChunk, getStrength(),
_iChunk, _count, _fnicj, _rStrength);
}
/**
* Set the log transformed strength ? if strength learning is enabled in the
* model, the strength value will not remain fixed.
*
* @param s
* The new strength value
*/
public void setStrength(double s)
{
_strength = s;
_rStrength = Math.exp(s);
dirty();
}
/**
* Sets the count attribute of the Link object
*
* @param count
* The new count value
*/
public void setCount(int count)
{
_count = count;
dirty();
}
/**
* increment the count number of links this Link represents
*
* @returns the new count
*/
public void increment()
{
_count++;
dirty();
}
/**
* decrement the count number of links this Link represents.
*
* @return Description of the Return Value
* @returns the new count.
*/
public int decrement()
{
_count--;
dirty();
return _count;
}
/**
* Description of the Method
*/
public void resetStrength()
{
_rStrength = computeDefaultRStrength();
_strength = Math.log(_rStrength);
clean();
}
/**
* Description of the Method
*/
protected void computeStrength()
{
_rStrength = computeLearnedRStrength();
_strength = Math.log(_rStrength);
clean();
}
/**
* Gets the dirty attribute of the Link object
*
* @return The dirty value
*/
protected boolean isDirty()
{
return _timeStamp < ACTRRuntime.getRuntime().getClock(_iChunk.getModel())
.getTime()
|| Double.isNaN(_rStrength) || Double.isNaN(_strength);
}
/**
* Description of the Method
*/
protected void dirty()
{
_timeStamp = -1;
}
/**
* Description of the Method
*/
protected void clean()
{
_timeStamp = ACTRRuntime.getRuntime().getClock(_iChunk.getModel())
.getTime();
}
/**
* Description of the Method
*
* @return Description of the Return Value
*/
protected double computeDefaultRStrength()
{
// self link
if (_iChunk.equals(_jChunk)) return 1;
int totalChunksInMemory = (int) _jChunk.getModel().getDeclarativeModule()
.getNumberOfChunks();
int fan = 0;
if (_jChunk.getSubsymbolicChunk() instanceof ISubsymbolicChunk4)
fan = ((ISubsymbolicChunk4) _jChunk.getSubsymbolicChunk())
.getNumberOfIAssociations();
double dRji = 0;
if (fan > 0)
dRji = (double) totalChunksInMemory / (double) fan;
else if (fan == 0) dRji = 1.0;
/*
* number of duplicate references, scales the default r accordingly
*/
if (_count > 0) dRji *= _count;
if (LOGGER.isDebugEnabled())
LOGGER.debug(this + " defaultRji : " + dRji + "(" + Math.log(dRji)
+ ") fan : " + fan + " count : " + _count + " totalFacts : "
+ totalChunksInMemory);
return dRji;
}
/**
* Description of the Method
*
* @return Description of the Return Value
*/
protected double computeLearnedRStrength()
{
if (_iChunk.equals(_jChunk)) return 1;
IModel m = _iChunk.getModel();
IDeclarativeLearningModule4 idlm = (IDeclarativeLearningModule4) m
.getModule(IDeclarativeLearningModule4.class);
if (idlm == null || idlm.isAssociativeLearningEnabled()) return _rStrength;
ISubsymbolicChunk4 j = (ISubsymbolicChunk4) _jChunk.getSubsymbolicChunk();
ISubsymbolicChunk4 i = (ISubsymbolicChunk4) _iChunk.getSubsymbolicChunk();
double numerator = idlm.getAssociativeLearning() * _rStrength;
double denom = idlm.getAssociativeLearning() * j.getTimesInContext();
double fcEji = 1.0;
if (i.getTimesNeeded() == 0)
fcEji = j.getTimesInContext();
else
fcEji = getFNICJ() * (m.getCycle() - i.getCreationCycle())
/ i.getTimesNeeded();
numerator += fcEji;
if (LOGGER.isDebugEnabled())
LOGGER.debug("numerator : " + numerator + " denom : " + denom
+ " fcEji : " + fcEji);
return numerator / denom;
}
/**
* Reset all the Link strengths in a given model.
*
* @param m
* Description of the Parameter
*/
public static void resetAllLinks(IModel m)
{
try
{
for (IChunk chunk : m.getDeclarativeModule().getChunks().get())
{
ISubsymbolicChunk ssc = chunk.getSubsymbolicChunk();
if (ssc instanceof ISubsymbolicChunk4)
for (Link link : ((ISubsymbolicChunk4) ssc).getIAssociations(null))
// if (Math.abs(link.getStrength()) <= 0.0001)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("Reseting strength of " + link);
link.resetStrength();
}
}
}
catch (Exception e)
{
LOGGER.error("Could not reset links because of an exception ", e);
}
}
}