# Usage

### Maven POM

The following dependency includes the Aika software library to an existing maven project.

<dependency>
<groupId>org.aika-software</groupId>
<artifactId>aika</artifactId>
<version>0.17</version>
</dependency>

### Named Entity Recognition / Entity Resolution example

This example demonstrates the recognition of the words 'jackson cook' as forename and surname. If these words would have occurred independently, they would have been recognized as city or profession based on the example code. The complete code for this example can be found in the class NamedEntityRecognitionTest.
Now, the first step is to create an empty model. The following map is used as a dictionary to look up the individual word input neurons.

        Model m = new Model();

        HashMap<String, Neuron> inputNeurons = new HashMap<>();

        for(String word: new String[] {"jackson", "cook"}) {
            Neuron in = m.createNeuron("W-" + word);

            inputNeurons.put(word, in);
        }

These three neurons are just placeholders that will be wired up later on.

        Neuron forenameCategory = m.createNeuron("C-forename");
        Neuron surnameCategory = m.createNeuron("C-surname");
        Neuron inhibitingN = m.createNeuron("INHIB");

The following code snippet is the crucial section of this example. It shows how the first entity neuron is created and connected by its input synapses. The entity neurons represent the concrete meanings of the input words. As can be seen here the individual inputs have a few more properties than we are used to from other neural networks. The reason for this is that Aika not only propagates the input activation value to the next neuron but also the text range, the word position.
This entity neuron has three inputs. The first one simply checks if the word 'cook' occurred in the text. The second one is a positive feedback loop and checks if the previous word was a forename. The last one is a negative feedback loop. It makes sure that only one meaning is assigned to each word.
The other entity neurons are created in a similar fashion which is therefore not shown here.

        Neuron cookSurnameEntity = Neuron.init(
                m.createNeuron("E-cook (surname)"),
                3.0, // adjusts the bias
                RECTIFIED_HYPERBOLIC_TANGENT,
                EXCITATORY,
                new Synapse.Builder() // Requires the word to be recognized
                        .setSynapseId(0)
                        .setNeuron(inputNeurons.get("cook"))
                        .setWeight(10.0)
                        // This input requires the input activation to have an
                        // activation value of at least 0.9
                        .setBias(-10.0)
                        .setRecurrent(false)
                        .setRangeOutput(Range.Output.DIRECT),
                new Synapse.Builder() // The previous word needs to be a forename
                        .setSynapseId(1)
                        .setNeuron(forenameCategory)
                        .setWeight(5.0)
                        .setBias(0.0)
                        .addRangeRelation(END_TO_BEGIN_EQUALS, 0) // references the previous word
                        .setRecurrent(true) // this input is a positive feedback loop
                        .setRangeOutput(false),

                        // This neuron may be suppressed by the E-cook (profession) neuron, but there is no
                        // self suppression taking place even though 'E-cook (surname)' is also contained
                        // in the inhibitingN.
                new Synapse.Builder()
                        .setSynapseId(2)
                        .setNeuron(inhibitingN)
                        .setWeight(-100.0)
                        .setBias(0.0)
                        .setRecurrent(true) // this input is a negative feedback loop
                        .addRangeRelation(OVERLAPS, 0)
        );

The category neurons in this example only have input. For a real application there would be a long list of inputs.

        Neuron.init(
                forenameCategory,
                0.0,
                RECTIFIED_LINEAR_UNIT,
                EXCITATORY,
                new Synapse.Builder() // In this example there is only one forename considered.
                        .setSynapseId(0)
                        .setNeuron(jacksonForenameEntity)
                        .setWeight(1.0)
                        .setBias(0.0)
                        .setRangeOutput(Range.Output.DIRECT)
        );
        Neuron.init(
                surnameCategory,
                0.0,
                RECTIFIED_LINEAR_UNIT,
                EXCITATORY,
                new Synapse.Builder()
                        .setSynapseId(0)
                        .setNeuron(cookSurnameEntity)
                        .setWeight(1.0)
                        .setBias(0.0)
                        .setRangeOutput(Range.Output.DIRECT)
        );

The last neuron is the suppressing neuron. It is used as a mediator in order to avoid having to connect all of these neurons with each other. Self suppression does not take place.

        Neuron.init(
                inhibitingN,
                0.0,
                RECTIFIED_LINEAR_UNIT,
                INHIBITORY,
                new Synapse.Builder().setNeuron(cookProfessionEntity)
                        .setSynapseId(0)
                        .setWeight(1.0)
                        .setBias(0.0)
                        .setRangeOutput(true, true),
                new Synapse.Builder()
                        .setSynapseId(1)
                        .setNeuron(cookSurnameEntity)
                        .setWeight(1.0)
                        .setBias(0.0)
                        .setRangeOutput(true, true),
                new Synapse.Builder()
                        .setSynapseId(2)
                        .setNeuron(jacksonCityEntity)
                        .setWeight(1.0)
                        .setBias(0.0)
                        .setRangeOutput(true, true),
                new Synapse.Builder()
                        .setSynapseId(3)
                        .setNeuron(jacksonForenameEntity)
                        .setWeight(1.0)
                        .setBias(0.0)
                        .setRangeOutput(true, true)
            );

Now that the model is complete, we can start processing an actual text.

        Document doc = m.createDocument("mr. jackson cook was born in new york ");

        int i = 0;
        for(String w: doc.getContent().split(" ")) {
            int j = i + w.length();

            // Feed the individual words as inputs into the network.
            inputNeurons.get(w).addInput(doc, i, j);
            i = j + 1;
        }

        // Search for the best interpretation of this text.
        doc.process();

        System.out.println(doc.activationsToString());

        doc.clearActivations();

The output of this example looks as following. Each line in the table represents a neuron activation.

    // Activation ID - Final Decision - Range - Neuron Label - Upper Bound
    // - Final Value | Final Weight | Final Norm - Input Value | Target Value

        ...
    7  - SELECTED - (4,11)  - INHIB                - UB:2.0 - V:1.0 W:0.0 N:0.0
    3  - EXCLUDED - (4,11)  - E-jackson (city)     - UB:1.0 -
    10 - SELECTED - (4,11)  - C-forename           - UB:1.0 - V:1.0 W:0.0 N:0.0
    1  - SELECTED - (4,11)  - W-jackson            - UB:1.0 - V:1.0 W:0.0 N:0.0 - IV:1.0
    5  - SELECTED - (4,11)  - E-jackson (forename) - UB:1.0 - V:1.0 W:8.0 N:8.0
    20 - SELECTED - (12,16) - INHIB                - UB:2.0 - V:1.0 W:0.0 N:0.0
    16 - EXCLUDED - (12,16) - E-cook (profession)  - UB:1.0 -
    23 - SELECTED - (12,16) - C-surname            - UB:1.0 - V:1.0 W:0.0 N:0.0
    14 - SELECTED - (12,16) - W-cook               - UB:1.0 - V:1.0 W:0.0 N:0.0 - IV:1.0
    18 - SELECTED - (12,16) - E-cook (surname)     - UB:1.0 - V:1.0 W:8.0 N:8.0
        ...

     Final SearchNode:27  WeightSum:W:16.0 N:26.0

### Mutual exclusion example

        @Test
        public void testMutualExclusion() {
            Model m = new Model();

            // Create the input neurons for the network.
            Neuron inA = m.createNeuron("IN-A");
            Neuron inB = m.createNeuron("IN-B");
            Neuron inC = m.createNeuron("IN-C");

            // Instantiate the inhibitory neuron. Its inputs will be added later on.
            Neuron inhibN = m.createNeuron("INHIB");

            // Create three neurons that might be suppressed by the inhibitory neuron.
            Neuron pA = Neuron.init(
                    m.createNeuron("A"),
                    3.0,
                    RECTIFIED_HYPERBOLIC_TANGENT,
                    EXCITATORY,
                    new Synapse.Builder()
                            .setSynapseId(0)
                            .setNeuron(inA)
                            .setWeight(10.0)
                            .setBias(-10.0)
                            .setRecurrent(false)
                            .setRangeOutput(Range.Output.DIRECT),
                    new Synapse.Builder()
                            .setSynapseId(1)
                            .setNeuron(inhibN)
                            .setWeight(-100.0)
                            .setBias(0.0)
                            .setRecurrent(true)
                            .addRangeRelation(EQUALS, 0)
            );

            Neuron pB = Neuron.init(
                    m.createNeuron("B"),
                    5.0,
                    RECTIFIED_HYPERBOLIC_TANGENT,
                    EXCITATORY,
                    new Synapse.Builder()
                            .setSynapseId(0)
                            .setNeuron(inB)
                            .setWeight(10.0)
                            .setBias(-10.0)
                            .setRecurrent(false)
                            .setRangeOutput(Range.Output.DIRECT),
                    new Synapse.Builder()
                            .setSynapseId(1)
                            .setNeuron(inhibN)
                            .setWeight(-100.0)
                            .setBias(0.0)
                            .setRecurrent(true)
                            .addRangeRelation(EQUALS, 0)
            );

            Neuron pC = Neuron.init(
                    m.createNeuron("C"),
                    2.0,
                    RECTIFIED_HYPERBOLIC_TANGENT,
                    EXCITATORY,
                    new Synapse.Builder()
                            .setSynapseId(0)
                            .setNeuron(inC)
                            .setWeight(10.0)
                            .setBias(-10.0)
                            .setRecurrent(false)
                            .setRangeOutput(Range.Output.DIRECT),
                    new Synapse.Builder()
                            .setSynapseId(1)
                            .setNeuron(inhibN)
                            .setWeight(-100.0)
                            .setBias(0.0)
                            .setRecurrent(true)
                            .addRangeRelation(EQUALS, 0)
            );

            // Finally addInput all the inputs to the suppressing neuron.
            Neuron.init(
                    inhibN,
                    0.0,
                    RECTIFIED_LINEAR_UNIT,
                    INHIBITORY,
                    new Synapse.Builder()
                            .setSynapseId(0)
                            .setNeuron(pA)
                            .setWeight(1.0)
                            .setBias(0.0)
                            .setRecurrent(false)
                            .setRangeOutput(Range.Output.DIRECT),
                    new Synapse.Builder()
                            .setSynapseId(1)
                            .setNeuron(pB)
                            .setWeight(1.0)
                            .setBias(0.0)
                            .setRecurrent(false)
                            .setRangeOutput(Range.Output.DIRECT),
                    new Synapse.Builder()
                            .setSynapseId(2)
                            .setNeuron(pC)
                            .setWeight(1.0)
                            .setBias(0.0)
                            .setRecurrent(false)
                            .setRangeOutput(Range.Output.DIRECT)
            );

            Neuron outN = Neuron.init(m.createNeuron("OUT"),
                    0.0,
                    RECTIFIED_HYPERBOLIC_TANGENT,
                    EXCITATORY,
                    new Synapse.Builder()
                             .setSynapseId(0)
                            .setNeuron(pB)
                            .setWeight(1.0)
                            .setBias(0.0)
                            .setRecurrent(false)
                            .setRangeOutput(Range.Output.DIRECT)
            );

            // Now that the model is complete, apply it to a document.

            Document doc = m.createDocument("foobar", 0);

            // Add input activations starting at char 0 and ending at char 1
            // These inputs will be immediately propagated through the network.
            inA.addInput(doc, 0, 1);
            inB.addInput(doc, 0, 1);
            inC.addInput(doc, 0, 1);

            // Computes the best interpretation
            doc.process();

            System.out.println(doc.activationsToString(true, false, true));

            Assert.assertTrue(pA.getFinalActivations(doc).isEmpty());
            Assert.assertFalse(pB.getFinalActivations(doc).isEmpty());
            Assert.assertTrue(pC.getFinalActivations(doc).isEmpty());

            Assert.assertFalse(outN.getFinalActivations(doc).isEmpty());
            doc.clearActivations();
        }

    // Activation ID - Final Decision - Range - Neuron Label - Upper Bound
    // - Final Value | Final Weight | Final Norm - Input Value | Target Value

    0  - SELECTED - (0,1) - IN-A  - UB:1.0   - V:1.0 W:0.0 N:0.0 - IV:1.0
    6  - SELECTED - (0,1) - IN-B  - UB:1.0   - V:1.0 W:0.0 N:0.0 - IV:1.0
    11 - SELECTED - (0,1) - IN-C  - UB:1.0   - V:1.0 W:0.0 N:0.0 - IV:1.0
    4  - SELECTED - (0,1) - INHIB - UB:2.959 - V:1.0 W:0.0 N:0.0
    2  - EXCLUDED - (0,1) - A     - UB:0.995 -
    8  - SELECTED - (0,1) - B     - UB:1.0   - V:1.0 W:5.0 N:5.0
    13 - EXCLUDED - (0,1) - C     - UB:0.964 -
    10 - SELECTED - (0,1) - OUT   - UB:0.762 - V:0.762 W:0.0 N:0.0

     Final SearchNode:10  WeightSum:W:5.0 N:10.0

### Pattern matching example

        @Test
        public void testPatternMatching() {
            Model m = new Model();

            Map<Character, Neuron> inputNeurons = new HashMap<>();

            // Create an input neuron and a recurrent neuron for every letter in this example.
            for(char c: new char[] {'a', 'b', 'c', 'd', 'e', 'f'}) {
                Neuron in = m.createNeuron(c + "");

                inputNeurons.put(c, in);
            }

            Neuron pattern = Neuron.init(
                    m.createNeuron("BCDE"),
                    5.0,
                    RECTIFIED_HYPERBOLIC_TANGENT,
                    EXCITATORY,
                    new Synapse.Builder()
                            .setSynapseId(0)
                            .setNeuron(inputNeurons.get('b'))
                            .setWeight(10.0)
                            .setBias(-10.0)
                            .setRecurrent(false)
                            .addRangeRelation(Range.Relation.END_TO_BEGIN_EQUALS, 1)
                            .setRangeOutput(Range.Mapping.BEGIN, Range.Mapping.NONE),
                    new Synapse.Builder()
                            .setSynapseId(1)
                            .setNeuron(inputNeurons.get('c'))
                            .setWeight(10.0)
                            .setBias(-10.0)
                            .setRecurrent(false)
                            .addRangeRelation(Range.Relation.END_TO_BEGIN_EQUALS, 2),
                    new Synapse.Builder()
                            .setSynapseId(2)
                            .setNeuron(inputNeurons.get('d'))
                            .setWeight(10.0)
                            .setBias(-10.0)
                            .setRecurrent(false)
                            .addRangeRelation(Range.Relation.END_TO_BEGIN_EQUALS, 3),
                    new Synapse.Builder()
                            .setSynapseId(3)
                            .setNeuron(inputNeurons.get('e'))
                            .setWeight(10.0)
                            .setBias(-10.0)
                            .setRecurrent(false)
                            .setRangeOutput(false, true)
            );

            // Create a simple text document.
            Document doc = m.createDocument("a b c d e ", 0);

            // Then add the characters
            for(int i = 0; i < doc.length(); i++) {
                char c = doc.getContent().charAt(i);
                if(c != ' ') {
                    inputNeurons.get(c).addInput(doc, i, i + 1);
                }
            }

            // Computes the best interpretation
            doc.process();

            Assert.assertEquals(1, pattern.getActivations(doc, false).size());

            System.out.println("Output activation:");
            INeuron n = pattern.get();
            for(NodeActivation act: n.getActivations(doc, true)) {
                System.out.println("Text Range: " + act.key.range);
                System.out.println("Node: " + act.key.node);
                System.out.println();
            }

            System.out.println("All activations:");
            System.out.println(doc.activationsToString(true, false, true));
            System.out.println();

            doc.clearActivations();
        }

    Output activation:
    Text Range: (2,9)
    Node: BCDE - OR[0:AND[(-1:I[3,b|),(1:I|5,c|),(2:I|7,d|),(3:I|9,e])]]

    All activations:
    //Activation ID - Final Decision - Range - Neuron Label
    //- Upper Bound - Final Value | Final Weight | Final Norm - Input Value | Target Value

    0  - SELECTED - (0,1) - a    - UB:1.0 - V:1.0 W:0.0 N:0.0 - IV:1.0
    1  - SELECTED - (2,3) - b    - UB:1.0 - V:1.0 W:0.0 N:0.0 - IV:1.0
    20 - SELECTED - (2,9) - BCDE - UB:1.0 - V:1.0 W:0.0 N:0.0
    3  - SELECTED - (4,5) - c    - UB:1.0 - V:1.0 W:0.0 N:0.0 - IV:1.0
    6  - SELECTED - (6,7) - d    - UB:1.0 - V:1.0 W:0.0 N:0.0 - IV:1.0
    11 - SELECTED - (8,9) - e    - UB:1.0 - V:1.0 W:0.0 N:0.0 - IV:1.0

     Final SearchNode:1  WeightSum:W:0.0 N:0.0