Java tutorial
/*********************************************************************** This file is part of KEEL-software, the Data Mining tool for regression, classification, clustering, pattern mining and so on. Copyright (C) 2004-2010 F. Herrera (herrera@decsai.ugr.es) L. Snchez (luciano@uniovi.es) J. Alcal-Fdez (jalcala@decsai.ugr.es) S. Garca (sglopez@ujaen.es) A. Fernndez (alberto.fernandez@ujaen.es) J. Luengo (julianlm@decsai.ugr.es) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ **********************************************************************/ package keel.Algorithms.Neural_Networks.NNEP_Common.neuralnet; import java.util.ArrayList; import java.util.Arrays; import javolution.xml.XmlElement; import javolution.xml.XmlFormat; import net.sf.jclec.util.random.IRandGen; import net.sf.jclec.util.range.Interval; import org.apache.commons.lang.builder.HashCodeBuilder; /** * <p> * @author Written by Pedro Antonio Gutierrez Penya, Aaron Ruiz Mora (University of Cordoba) 17/07/2007 * @version 0.1 * @since JDK1.5 * </p> */ public abstract class LinkedNeuron implements INeuron { /** * <p> * Base implementation of a neuron of a hidden or output layer * </p> */ ///////////////////////////////////////////////////////////////// // ------------------------------------- Marshal/unmarshal format ///////////////////////////////////////////////////////////////// /** * <p> * Marshal/Unmarshal links and and a boolean indicating if the * neuron is input-biased * </p> */ protected static final javolution.xml.XmlFormat<LinkedNeuron> XML = new XmlFormat<LinkedNeuron>( LinkedNeuron.class) { public void format(LinkedNeuron source, XmlElement xml) { // Marshal biased xml.setAttribute("biased", source.biased); // Marshal each link xml.add(new ArrayList<Link>(Arrays.asList(source.links)), "links"); // Marshal weightRange xml.add(source.weightRange, "weight-range"); } public LinkedNeuron parse(XmlElement xml) { // Resulting object LinkedNeuron result = (LinkedNeuron) xml.object(); // Unmarshal biased result.biased = xml.getAttribute("biased", false); // Unmarshal each link ArrayList<Link> list = xml.<ArrayList<Link>>get("links"); result.links = list.toArray(new Link[list.size()]); // Unmarshal weightRange result.weightRange = xml.<Interval>get("weight-range"); // Return result return result; } public String defaultName() { return "linked-neuron"; } }; ///////////////////////////////////////////////////////////////// // --------------------------------------------------- Attributes ///////////////////////////////////////////////////////////////// /** Link array */ protected Link[] links; /** Is biased? */ protected boolean biased; /** Weight range */ protected Interval weightRange; ///////////////////////////////////////////////////////////////// // -------------------------------------------------- Constructor ///////////////////////////////////////////////////////////////// /** * <p> * Empty constructor * </p> */ public LinkedNeuron() { super(); } ///////////////////////////////////////////////////////////////// // ------------------------------- Getting and setting attributes ///////////////////////////////////////////////////////////////// /** * <p> * Returns the links array * </p> * @return ILink [] Links array */ public Link[] getLinks() { return links; } /** * <p> * Sets the links of the neuron * </p> * @param links [] New links array */ public void setLinks(Link links[]) { this.links = links; } /** * <p> * Returns the link with the neuron specified (0 is bias neuron) * </p> * @return Link Link specified */ public Link getLink(int neuron) { return links[neuron]; } /** * <p> * Sets the link with the neuron specified (0 is bias neuron) * </p> * @param neuron Neuron to set the link * @param link New link of the neuron specified */ public void setLink(int neuron, Link link) { links[neuron] = link; } /** * <p> * Returns a boolean indicating if the layer has a bias neuron * </p> * @return true if the layer has a bias neuron */ public boolean isBiased() { return biased; } /** * <p> * Sets a boolean indicating if the layer has a bias neuron * </p> * @param biased Boolean has bias neuron */ public void setBiased(boolean biased) { this.biased = biased; } /** * <p> * Returns the weight range associated to the links * </p> * @return Interval Weight range */ public Interval getWeightRange() { return weightRange; } /** * <p> * Sets the weight range associated to the links * </p> * @param weightRange New weight range */ public void setWeightRange(Interval weightRange) { this.weightRange = weightRange; } ///////////////////////////////////////////////////////////////// // ------------------------------- Implementing INeuron interface ///////////////////////////////////////////////////////////////// /** * <p> * Checks if this neuron is equal to another * </p> * @param other Other link to compare * @return true if both neurons are equal */ public boolean equals(INeuron other) { if (this.hashCode() != other.hashCode()) return false; else return true; } /** * <p> * Returns an integer number that identifies the neuron * </p> * @return int Hashcode */ public int hashCode() { HashCodeBuilder hcb = new HashCodeBuilder(31, 37); hcb.append(this.getClass().toString()); hcb.append(links); return hcb.toHashCode(); } /** * <p> * Operates this neuron, using an input array. This means: * </p> * <p> * 1) apply the input function to all the inputs neuron and sum the result * </p> * <p> * 2) apply the output function to the result * </p> * @param inputs Double array to be used for the inputs observations * @return double Output of the neuron for the array specified */ public double operate(double[] inputs) { //Result of the operation double result; //Init the input result = initInput(); //Apply the input function to all the inputs neurons and accumulate //the result double in; double weight; for (int i = 0; i < links.length; i++) { if (!links[i].isBroken() && biased && i == links.length - 1) { //Obtain the weight weight = links[i].getWeight(); //Apply the input function result = inputFunction(result, 1, weight); } else if (!links[i].isBroken()) { //Obtain the output array of the origin neuron //with the provided inputs in = links[i].getOrigin().operate(inputs); //Obtain the weight weight = links[i].getWeight(); //Apply the input function result = inputFunction(result, in, weight); } } //Apply the output function to the result result = outputFunction(result); //Return the result return result; } /** * <p> * Operates this neuron using an input matrix as argument * </p> * @param inputs Double matrix to be used for the inputs observations * @return double [] Array outputs of the neuron for the matrix specified */ public double[] operate(double[][] inputs) { //Result of the operation double[] result = new double[inputs[0].length]; //Init the input array for (int i = 0; i < result.length; i++) result[i] = initInput(); //Apply the input function to all the inputs neurons and accumulate //the result double[] ins; for (int i = 0; i < links.length; i++) { if (!links[i].isBroken() && biased && links[i].getOrigin() == null) { //Apply the input function for (int j = 0; j < result.length; j++) result[j] = inputFunction(result[j], 1, links[i].getWeight()); } else if (!links[i].isBroken()) { //Obtain the output array of the origin neuron //with the provided observations ins = links[i].getOrigin().operate(inputs); //Apply the input function for (int j = 0; j < result.length; j++) result[j] = inputFunction(result[j], ins[j], links[i].getWeight()); } } //Apply the output function to the result for (int i = 0; i < result.length; i++) result[i] = outputFunction(result[i]); //Return the result return result; } ///////////////////////////////////////////////////////////////// // ----------------------------------------------- Public methods ///////////////////////////////////////////////////////////////// /** * <p> * Returns the number of effective links of the neuron * </p> * @return int Number of effective links */ public int getNoflinks() { int noflinks = 0; for (int i = 0; i < links.length; i++) { if (!links[i].isBroken()) noflinks++; } return noflinks; } /** * <p> * Returns a random weight for a link * </p> * @param randGen Random number generator * @param significativeWeight Minimum absolute value of the new weight * @return double Random weight */ public double randomWeight(IRandGen randGen, double significativeWeight) { double weight = 0; do { weight = weightRange.getRandom(randGen); } while (Math.abs(weight) < significativeWeight); return weight; } /** * <p> * Keep relevant links, that is, those links whose weight is higher * than certain number * </p> * @param significativeWeight Significative weight */ public void keepRelevantLinks(double significativeWeight) { for (int i = 0; i < links.length; i++) { if (!links[i].isBroken() && Math.abs(links[i].getWeight()) < significativeWeight) { links[i].setWeight(0); links[i].setBroken(true); links[i].setOrigin(null); links[i].setTarget(null); } if (links[i].isBroken() && Math.abs(links[i].getWeight()) != 0) { links[i].setWeight(0); links[i].setOrigin(null); links[i].setTarget(null); } } } /** * <p> * Returns a copy of this linked neuron * </p> * @param previousLayer Previous layer to which copied neuron * is going to be linked * @return LinkedNeuron Copy of this linked neuron */ public LinkedNeuron copy(ILayer<? extends INeuron> previousLayer) { LinkedNeuron result = null; try { // Generate new neuron result = this.getClass().newInstance(); // Copy biased property result.setBiased(this.biased); // Copy weight range result.setWeightRange(this.weightRange); // Copy links of the neuron Link resultLinks[] = new Link[this.links.length]; for (int i = 0; i < this.links.length; i++) { // Generate new link resultLinks[i] = new Link(); // Set link properties resultLinks[i].setBroken(this.links[i].isBroken()); if (!resultLinks[i].isBroken()) { resultLinks[i].setWeight(this.links[i].getWeight()); if (this.links[i].getOrigin() == null) resultLinks[i].setOrigin(null); else resultLinks[i].setOrigin(previousLayer.getNeuron(i)); resultLinks[i].setTarget(result); } } result.setLinks(resultLinks); } catch (InstantiationException e) { System.out.println("Instantiation Error " + e.getLocalizedMessage()); e.printStackTrace(); } catch (IllegalAccessException e) { System.out.println("Illegal Access Error " + e.getLocalizedMessage()); e.printStackTrace(); } return result; } ///////////////////////////////////////////////////////////////// // --------------------------------------------- Abstract methods ///////////////////////////////////////////////////////////////// /** * <p> * Init the input of the neuron (0 or 1 depending on the kind of neuron) * </p> * @return double Initialized value of the input */ protected abstract double initInput(); /** * <p> * Input function of the neuron. Update input for each input neuron * </p> * @param input Old input * @param in Output of the input neuron * @param weight Weight of the link to the input neuron * @return double Partial input of the input neuron */ protected abstract double inputFunction(double input, double in, double weight); /** * <p> * Output function of the neuron * </p> * @param input Input of the neuron * @return double output of the neuron */ protected abstract double outputFunction(double input); }