/**
* 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 java.util.ArrayList;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.chunk.IChunk;
import org.jactr.core.chunk.link.AbstractAssociativeLink;
import org.jactr.core.chunk.link.IAssociativeLink;
import org.jactr.core.chunk.link.IAssociativeLinkEquation;
import org.jactr.core.utils.parameter.ParameterHandler;
/**
* 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 Chunki's slots contains Chunkj 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 Link4 extends AbstractAssociativeLink
{
static public final String COUNT_PARAM = "Count";
static public final String FNICJ_PARAM = "FNiCj";
private static transient Log LOGGER = LogFactory.getLog(Link4.class
.getName());
/**
* Number of times this link is used
*/
private int _count;
/**
* prior strength
*/
private double _rStrength = 1;
/**
* count of the times that i was accessed while j was in context
*/
private double _fnicj;
/**
* to permit lazy computation of strength
*/
private double _timeStamp = -1;
private IAssociativeLinkEquation _linkEquation;
/**
* The j chunk should contain the I chunk as a slot value.
*/
public Link4(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 Link4(IChunk j, IChunk i, int count, double strength)
{
super(j, i, strength);
_count = count;
}
public void setAssociativeLinkEquation(IAssociativeLinkEquation equation)
{
_linkEquation = equation;
}
public IAssociativeLinkEquation getAssociativeLinkEquation()
{
return _linkEquation;
}
/**
* 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;
// }
/**
* 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();
}
public void setFNICJ(double FNiCj)
{
_fnicj = FNiCj;
dirty();
}
/**
* Returns the R strength which is the prelog transformed strength value
*
* @return The rStrength value
*/
public double getRStrength()
{
return _rStrength;
}
public void setRStrength(double r)
{
_rStrength = r;
dirty();
}
@Override
public double getStrength()
{
return getStrength(true);
}
public double getStrength(boolean recompute)
{
if (recompute && isDirty()) computeStrength();
return super.getStrength();
}
/**
* Description of the Method
*
* @return Description of the Return Value
*/
@Override
public String toString()
{
return String.format("j:%s - i:%s", getJChunk(), getIChunk());
}
/**
* 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
*/
@Override
public void setStrength(double s)
{
super.setStrength(s);
_rStrength = Math.exp(s);
}
/**
* 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
*/
protected void computeStrength()
{
clean();
double strength = 0;
if (_linkEquation != null)
strength = _linkEquation.computeLearnedStrength(this);
if (Double.isNaN(strength)) strength = 0;
setStrength(strength);
}
/**
* Gets the dirty attribute of the Link object current will recalculate only
* once per cycle.
*
* @return The dirty value
*/
public boolean isDirty()
{
return _timeStamp < 0 // getIChunk().getModel().getAge()
|| Double.isNaN(_rStrength);
}
/**
* Description of the Method
*/
public void dirty()
{
_timeStamp = -1;
}
/**
* Description of the Method
*/
protected void clean()
{
_timeStamp = getIChunk().getModel().getAge();
}
@Override
public void copy(IAssociativeLink link) throws IllegalArgumentException
{
super.copy(link);
if (link instanceof Link4)
{
Link4 l4 = (Link4) link;
setRStrength(l4.getRStrength());
setCount(l4.getCount());
setFNICJ(l4.getFNICJ());
l4.dirty();
}
}
@Override
public Collection<String> getPossibleParameters()
{
ArrayList<String> rtn = new ArrayList<String>(3);
rtn.addAll(super.getPossibleParameters());
rtn.add(COUNT_PARAM);
rtn.add(FNICJ_PARAM);
return rtn;
}
@Override
public String getParameter(String key)
{
if (COUNT_PARAM.equalsIgnoreCase(key))
return Integer.toString(getCount());
else if (FNICJ_PARAM.equalsIgnoreCase(key))
return Double.toString(getFNICJ());
else
return super.getParameter(key);
}
@Override
public void setParameter(String key, String value)
{
if (COUNT_PARAM.equalsIgnoreCase(key))
setCount(ParameterHandler.numberInstance().coerce(value).intValue());
else if (FNICJ_PARAM.equalsIgnoreCase(key))
setFNICJ(ParameterHandler.numberInstance().coerce(value).doubleValue());
else
super.setParameter(key, value);
}
}