5
\$\begingroup\$

I have created a neural network in Java, it contains multiple classes. I have uploaded the documentation for the network here: Doxygen, and the full source can be found on Github.

Let me start out by saying that I'm creating this network to be used as an API, and it is far from finished, but I have created the first working version of it.

That said, The audience of this project is other programmers who want to simply write an input stream, and an algorithm that determines fitness, and have an engine do all of the heavy lifting.

That said, here is my starting class, sans some testing methods, which contains an example application of the network in its current state:

package neurIO;
import neurIO.data.bitInput.BitInput;
import neurIO.data.bitInput.BitInputSet;
import neurIO.engine.Column;
import neurIO.engine.Network;
import neurIO.system.Neuron;
import neurIO.system.Node;
import neurIO.system.OutputNode;
import neurIO.system.Truth;
import neurIO.system.TruthTable;
/**
 * Start
 * ===
 *  
 *This is the main class to start tests, GUI, etc.
 **/
public class Start {

    /**
     *  The main program used to start the GUI, do tests, etc. 
     *  This is NOT for API usage.  
     **/
    public static void main(String[] args) {
        Global.args = args;
        printHelp();
        printNetworkTest();
        switch(args[0]){
            default : printHelp();
        }
    }

    /**
     * Doesn't actually print any help at the moment...
     * 
     * will eventually print out a command line help form
     **/
    public static void printHelp(){
        System.out.println("NEURIO 2017 Brice Johnson");
        System.out.println("-----------------------------");
        System.out.println("           OPTIONS           ");
        System.out.println("-----------------------------");
    }



    /**
     * Prints out the result of a summing network which sums one byte at a time
     **/
    private static void printNetworkTest(){
        boolean[] xor = {false,true,true,false};
        boolean[] and = {false,false,false,true};
        boolean[] sum = {false,true,true,false,true,false,false,true};
        boolean[] car = {false,false,false,true,false,true,true,true};

        Network eightBitAdder = new Network(8,2);
        BitInput A1 = new BitInput(false),
        A2 = new BitInput(false),
        A3 = new BitInput(false),
        A4 = new BitInput(false),
        A5 = new BitInput(false),
        A6 = new BitInput(false),
        A7 = new BitInput(false),
        A8 = new BitInput(false),
        B1 = new BitInput(false),
        B2 = new BitInput(false),
        B3 = new BitInput(false),
        B4 = new BitInput(false),
        B5 = new BitInput(false),
        B6 = new BitInput(false),
        B7 = new BitInput(false),
        B8 = new BitInput(false);
        BitInput[] num1 = {A1,A2,A3,A4,A5,A6,A7,A8};
        BitInput[] num2 = {B1,B2,B3,B4,B5,B6,B7,B8};
        BitInput[] nums = {A1,A2,A3,A4,A5,A6,A7,A8,B1,B2,B3,B4,B5,B6,B7,B8};
        BitInputSet bus1 = new BitInputSet(num1);
        BitInputSet bus2 = new BitInputSet(num2);

        Column in = new Column(nums);

        eightBitAdder.input = in;

        bus1.numToInput(234);
        bus2.numToInput(154);

        Node[] bitIns1 = {A1,B1};  //Add the first bit of numbers A and B together.
        Neuron sum1= new Neuron(bitIns1, new TruthTable(xor)); //Get the sum of the bits
        Neuron car1= new Neuron(bitIns1, new TruthTable(and)); //Get the carry of the bits
        Column c1 = (new Column(sum1,car1)); // Make into a function
        eightBitAdder.addColumn(c1);// Add to a network
        Node[] bitIns2 = {A2,B2,car1};//Add the last carry, and the second bits together.
        Neuron sum2 = new Neuron(bitIns2,new TruthTable(sum));// Different truth tables are used for 3 bits.
        Neuron car2 = new Neuron(bitIns2,new TruthTable(car));
        Column c2 = (new Column(sum2,car2));
        eightBitAdder.addColumn(c2);
        Node[] bitIns3 = {A3,B3,car2};
        Neuron sum3 = new Neuron(bitIns3,new TruthTable(sum));
        Neuron car3 = new Neuron(bitIns3,new TruthTable(car));
        Column c3 = (new Column(sum3,car3));
        eightBitAdder.addColumn(c3);
        Node[] bitIns4 = {A4,B4,car3};
        Neuron sum4 = new Neuron(bitIns4,new TruthTable(sum));
        Neuron car4 = new Neuron(bitIns4,new TruthTable(car));
        Column c4 = (new Column(sum4,car4));
        eightBitAdder.addColumn(c4);
        Node[] bitIns5 = {A5,B5,car4};
        Neuron sum5 = new Neuron(bitIns5,new TruthTable(sum));
        Neuron car5 = new Neuron(bitIns5,new TruthTable(car));
        Column c5 = (new Column(sum5,car5));
        eightBitAdder.addColumn(c5);
        Node[] bitIns6 = {A6,B6,car5};
        Neuron sum6 = new Neuron(bitIns6,new TruthTable(sum));
        Neuron car6 = new Neuron(bitIns6,new TruthTable(car));
        Column c6 = (new Column(sum6,car6));
        eightBitAdder.addColumn(c6);
        Node[] bitIns7 = {A7,B7,car6};
        Neuron sum7 = new Neuron(bitIns7,new TruthTable(sum));
        Neuron car7 = new Neuron(bitIns7,new TruthTable(car));
        Column c7 = (new Column(sum7,car7));
        eightBitAdder.addColumn(c7);
        Node[] bitIns8 = {A8,B8,car7};
        Neuron sum8 = new Neuron(bitIns8,new TruthTable(sum));
        Neuron car8 = new Neuron(bitIns8,new TruthTable(car));
        Column c8 = (new Column(sum8,car8));
        eightBitAdder.addColumn(c8);
        OutputNode[] outputNodes = OutputNode.makeOutputNodes(sum1,sum2,sum3,sum4,sum5,sum6,sum7,sum8,car8);
        Column outputColumn = new Column(outputNodes);
        eightBitAdder.output = outputColumn;

        int result = 0;
        for(int i = 0; i < outputNodes.length; i++){
            System.out.println(outputNodes[i].getValue());
            result +=(outputNodes[i].getValue()?1:0)<<i;
        }

        System.out.println(result);

    }
}

What I want to note here is the network test. In it's current state, it takes many lines in order to 'build' this 8-bit adder. That said, it's very automatable. I could create a loop that creates all of the neurons and have it be much shorter than the current setup.

What I'm afraid of is this being too robust, and requiring too much work to set up a network of one's own if they want to. I'm also afraid of working on this incorrectly. I have no neural network knowledge prior to this, with the exception of knowing that there being neurons, inputs, and outputs. Thus, i'm not sure if this implementation is 'too digital', un-metricable, and overall just unrefined.

There are three other classes I'd like input on.

/**
 * @author Brice Johnson
 * @version 0.01
 * @category Data
 **/
package neurIO.system;

/**
 * Neuron
 * ===
 * The basic unit of processing.
 * Uses references to children to look up its result on a truth table held in RAM.
 **/
public class Neuron extends Node {
    TruthTable truths;
    Node[] children;
    TruthTable function;
    int width;
    volatile boolean evaluated = false;
    volatile boolean value;//Volatility helps with evaluation.

    public Neuron(Node[] children, TruthTable function) {
        this.children = children;
        this.function = function;
        this.width = function.width;
        if (!this.isValid()) {

        }
    }

    public Neuron clone(Node[] newTargets){
        return new Neuron(newTargets, this.truths);
    }

    public boolean isValid() {
        return width == children.length;
    }

    @Override
    public boolean getValue() {
        if (!evaluated) {
            boolean[] ins = new boolean[width];
            for (int i = 0; i < width - 1; i++) {
                ins[i] = children[i].getValue();
            }
            value = function.getResult(ins);
            evaluated = true;
        }
        return value;
    }

    @Override
    public void resetValue() {
        evaluated = false;
    }

    public static class Presets {
        /**
         * Presets are commonly used nodes, here for ease of access.
         **/
        public static Neuron AND(Node in1, Node in2) {
            boolean[] truths = { false, false, false, true };
            Node[] inputs = { in1, in2 };
            return new Neuron(inputs, new TruthTable(truths));
        }

        public static Neuron OR(Node in1, Node in2) {
            boolean[] truths = { false, true, true, true };
            Node[] inputs = { in1, in2 };
            return new Neuron(inputs, new TruthTable(truths));
        }

        public static Neuron NOR(Node in1, Node in2) {
            boolean[] truths = { true, false, false, false };
            Node[] inputs = { in1, in2 };
            return new Neuron(inputs, new TruthTable(truths));
        }

        public static Neuron NAND(Node in1, Node in2) {
            boolean[] truths = { true, true, true, false };
            Node[] inputs = { in1, in2 };
            return new Neuron(inputs, new TruthTable(truths));
        }

        public static Neuron XOR(Node in1, Node in2) {
            boolean[] truths = { false, true, true, false };
            Node[] inputs = { in1, in2 };
            return new Neuron(inputs, new TruthTable(truths));
        }
    }
}

The neuron is an extension of the node class, which is very simple itself:

/**
 * @author Brice Johnson
 * @version 0.01
 * @category Data
 **/
package neurIO.system;

/**
 * Node
 * ===
 * A lower level class that is used in columns and networks
 * Allows for differentiation between Neurons(computational nodes), Inputs (constant nodes), and Outputs (resultant nodes)
 **/
public class Node {
    protected boolean value;

    public Node() {}

    public Node(boolean value) {
        this.value = value;
    }

    public boolean getValue(){
        return value;
    }
    public void resetValue(){
    }
}

A node has a value that can be given. It's not that complicated. The neuron is a node which uses other nodes to determine its value. The valid value of any node is 1 or 0.

You can see in neuron that it caches its result in a volatile variable. This is to keep the value in the CPU cache, which dramatically speeds up evaluation.

If the result has not been evaluated, then it gathers the children and makes a result based on a truth table function.

The final class that I'd like reviewed is the Truth table class:

/**
 * @author Brice Johnson
 * @version 0.01
 * @category Data
 **/
package neurIO.system;

import neurIO.Global;


public class Truth {
    /**
     * Truth
     * ===
     * A truth is a certain output given explicit inputs.
     * A truth can contain many inputs, but only one output
     * A list that contains outputs for every combination of inputs of length n is a truth table.
     **/
    boolean[] conditions;
    boolean result;
    public String name = "";
    short index = 0;

    public Truth(int width){
        conditions = new boolean[width];

    }

    public Truth(String name, int width, boolean result) {
        conditions = new boolean[width];
        this.result = result;
        this.name = name;
    }   

    public Truth(int width, boolean result) {
        conditions = new boolean[width];
        this.result = result;
    }   

    public Truth( boolean[] conditions, boolean result) {
        this.conditions = conditions;
        index = (short) (conditions.length-1);
        this.result = result;       
    }   

    public Truth(String name, boolean[] conditions, boolean result) {
        this.conditions = conditions;
        index = (short) (conditions.length-1);
        this.result = result;   
        this.name = name;
    }

    public int getWidth() {
        return conditions.length;
    }

    public Truth addParam(boolean param) {
        if(index < conditions.length) {
            conditions[index] = param;
            index++;
        }
        return this;            
    }

    @Override
    public String toString() {
        String tstr = "";
        short width = (short) (Math.max(this.getWidth()*2,name.length())+2);
        tstr += Global.rept("_", width)+'\n';
        tstr += "|"+Global.rept(" ", (width - name.length())/2)+name+Global.rept(" ",((width - name.length())/2)-2)+"|"+'\n';
        tstr += "|"+Global.rept("-", width-2)+"|"+'\n';
        tstr += "|"+generateStringHeader()+"|"+'\n';
        tstr += "|"+generateStringTruth()+"||"+'\n';
        tstr += "|"+Global.rept("-", width-2)+"|"+'\n';


        return tstr;
    }

    private String generateStringTruth(){
        String toReturn = "";
        for(int i = 0; i < getWidth(); i++) {
            toReturn+=conditions[i]?"1":"0"+"|";

        }
        return toReturn;
    }

    private String generateStringHeader(){
        String toReturn = "";
        for(int i = 0; i < getWidth(); i++) {
            toReturn+=colIndexName(i)+"|";
        }
        return toReturn;
    }

    public static String colIndexName(int index) {
        String toReturn = "";
        while(index >= 0){
            int indexLetter = (index%26);
            index /= 26;
            index--;
            toReturn = (char)(indexLetter+65)+toReturn;
        }
        return toReturn;
    }

}

my main concern with this class is memory optimizations. The arrays are always sorted, so any set of inputs from first to last will be its own index. the input set 'false true false true' will be the '0101'th (5th) index. I think that this is an efficient implementation, but i'd like some outside input.

\$\endgroup\$
  • 1
    \$\begingroup\$ I would suggest you to get the source for some of there ML libraries like keras and tensorflow and then come up with something similar so that you get a more detailed understanding of the kinds of API needed by the community. Always keep in mind that you are designing an API that is needed by somebody and move further from there. Note that neural networks work on double rather than int and the precision of these calculations is of utmost importance. First build a user friendly API, then implement the optimal solution with it. Strike a good balance between user-friendliness and performance. \$\endgroup\$ – MozenRath Dec 19 '17 at 6:50
  • \$\begingroup\$ @MozenRath any reason for double and int? \$\endgroup\$ – tuskiomi Dec 19 '17 at 15:11
  • \$\begingroup\$ The intermediate variables undergo complex mathematical operations and invariably result in fraction values \$\endgroup\$ – MozenRath Dec 19 '17 at 16:59
  • \$\begingroup\$ Why do you have a switch statement with only a default? I've never seen that before \$\endgroup\$ – dustytrash Apr 2 at 20:06
  • \$\begingroup\$ @dustytrash for later expansion. one day you'll be able to use command line flags / args. \$\endgroup\$ – tuskiomi Apr 3 at 5:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.