├── project
└── project.sbt
├── doc
├── DOT.gif
├── SOS.gif
├── bee.png
├── dna.png
├── neuron.gif
├── neuron.png
├── DOTLINE.gif
├── delay_gate.gif
├── evolution.png
├── signal_sum.gif
├── FurtherIdeas.pdf
├── SOS_animated.gif
├── sos_sequence.gif
├── dotline_animated.gif
├── DOTLINE_connected.gif
├── delaygate_animated.gif
├── signal_to_itself1.gif
├── signalsum_animated.gif
├── Apis_mellifera_flying.jpg
├── delay_gate_sequence.gif
├── signal_sum_sequence.gif
├── Westworld-Marsden-Wood.png
├── Wire_software_logo.svg.png
├── silenceable_delay_gate.gif
├── silenceable_signal_sum.gif
├── InstallAndUseInstructions.pdf
├── 2000px-Neuron-figure-notext.svg.png
├── ArtificialNeuralNetworksInAkka.pdf
├── 636105874145177141-Westworld-Marsden-Wood.jpg
├── ArtificialNeuralNetworksInAkka-abstract.pdf
├── ArtificialNeuralNetworksInAkka_ScalaPresentationSlides.odp
├── docker-install.txt
├── signal_sum.graphml
├── DOT.graphml
├── neuron.graphml
└── silenceable_signal_sum.graphml
├── src
├── main
│ ├── scala
│ │ └── anna
│ │ │ ├── async
│ │ │ ├── DummyNeuron.scala
│ │ │ ├── NeuronType.scala
│ │ │ ├── SilencingNeuron.scala
│ │ │ ├── Synapse.scala
│ │ │ ├── SchedulerBuffer.scala
│ │ │ ├── NeuronTriggers.scala
│ │ │ ├── NeuronRef.scala
│ │ │ ├── Messages.scala
│ │ │ ├── NeuronCounter.scala
│ │ │ ├── NetBuilderOps.scala
│ │ │ ├── NetRef.scala
│ │ │ ├── NetWrapper.scala
│ │ │ ├── Net.scala
│ │ │ ├── Neuron.scala
│ │ │ └── NetBuilder.scala
│ │ │ ├── logger
│ │ │ ├── HTMLLogOutput.scala
│ │ │ ├── StdoutLogOutput.scala
│ │ │ ├── LogOutput.scala
│ │ │ ├── StringLogOutput.scala
│ │ │ ├── SystemLogOutput.scala
│ │ │ ├── WhereAmICall.scala
│ │ │ ├── ListLogOutput.scala
│ │ │ ├── FileLogOutput.scala
│ │ │ └── LOG.scala
│ │ │ ├── utils
│ │ │ ├── IntRange.scala
│ │ │ ├── RandomNumber.scala
│ │ │ ├── DoubleRange.scala
│ │ │ └── Utils.scala
│ │ │ ├── data
│ │ │ ├── SynapseData.scala
│ │ │ ├── SynapseTrait.scala
│ │ │ ├── NeuronData.scala
│ │ │ └── NetData.scala
│ │ │ ├── blocks
│ │ │ ├── SignalSum.scala
│ │ │ ├── Sequencer.scala
│ │ │ └── DelayGate.scala
│ │ │ ├── Context.scala
│ │ │ └── Commands.scala
│ └── resources
│ │ └── application.conf
└── test
│ └── scala
│ └── anna
│ ├── async
│ ├── DotNetSuite.scala
│ ├── DelaySuite.scala
│ ├── NetSuite.scala
│ ├── NeuronTriggersSuite.scala
│ ├── LineNetSuite.scala
│ └── SilenceRequestSuite.scala
│ ├── blocks
│ ├── DelayGateSuite.scala
│ ├── SignalSumSuite.scala
│ ├── SequencerSuite.scala
│ └── SOSWithBlocksSuite.scala
│ └── data
│ ├── JsonSuite.scala
│ └── NetDataSuite.scala
├── .gitignore
└── README.md
/project/project.sbt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/DOT.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/DOT.gif
--------------------------------------------------------------------------------
/doc/SOS.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/SOS.gif
--------------------------------------------------------------------------------
/doc/bee.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/bee.png
--------------------------------------------------------------------------------
/doc/dna.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/dna.png
--------------------------------------------------------------------------------
/doc/neuron.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/neuron.gif
--------------------------------------------------------------------------------
/doc/neuron.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/neuron.png
--------------------------------------------------------------------------------
/doc/DOTLINE.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/DOTLINE.gif
--------------------------------------------------------------------------------
/doc/delay_gate.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/delay_gate.gif
--------------------------------------------------------------------------------
/doc/evolution.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/evolution.png
--------------------------------------------------------------------------------
/doc/signal_sum.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/signal_sum.gif
--------------------------------------------------------------------------------
/doc/FurtherIdeas.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/FurtherIdeas.pdf
--------------------------------------------------------------------------------
/doc/SOS_animated.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/SOS_animated.gif
--------------------------------------------------------------------------------
/doc/sos_sequence.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/sos_sequence.gif
--------------------------------------------------------------------------------
/doc/dotline_animated.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/dotline_animated.gif
--------------------------------------------------------------------------------
/doc/DOTLINE_connected.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/DOTLINE_connected.gif
--------------------------------------------------------------------------------
/doc/delaygate_animated.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/delaygate_animated.gif
--------------------------------------------------------------------------------
/doc/signal_to_itself1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/signal_to_itself1.gif
--------------------------------------------------------------------------------
/doc/signalsum_animated.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/signalsum_animated.gif
--------------------------------------------------------------------------------
/doc/Apis_mellifera_flying.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/Apis_mellifera_flying.jpg
--------------------------------------------------------------------------------
/doc/delay_gate_sequence.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/delay_gate_sequence.gif
--------------------------------------------------------------------------------
/doc/signal_sum_sequence.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/signal_sum_sequence.gif
--------------------------------------------------------------------------------
/doc/Westworld-Marsden-Wood.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/Westworld-Marsden-Wood.png
--------------------------------------------------------------------------------
/doc/Wire_software_logo.svg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/Wire_software_logo.svg.png
--------------------------------------------------------------------------------
/doc/silenceable_delay_gate.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/silenceable_delay_gate.gif
--------------------------------------------------------------------------------
/doc/silenceable_signal_sum.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/silenceable_signal_sum.gif
--------------------------------------------------------------------------------
/doc/InstallAndUseInstructions.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/InstallAndUseInstructions.pdf
--------------------------------------------------------------------------------
/doc/2000px-Neuron-figure-notext.svg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/2000px-Neuron-figure-notext.svg.png
--------------------------------------------------------------------------------
/doc/ArtificialNeuralNetworksInAkka.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/ArtificialNeuralNetworksInAkka.pdf
--------------------------------------------------------------------------------
/doc/636105874145177141-Westworld-Marsden-Wood.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/636105874145177141-Westworld-Marsden-Wood.jpg
--------------------------------------------------------------------------------
/doc/ArtificialNeuralNetworksInAkka-abstract.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/ArtificialNeuralNetworksInAkka-abstract.pdf
--------------------------------------------------------------------------------
/doc/ArtificialNeuralNetworksInAkka_ScalaPresentationSlides.odp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makingthematrix/ann/HEAD/doc/ArtificialNeuralNetworksInAkka_ScalaPresentationSlides.odp
--------------------------------------------------------------------------------
/src/main/scala/anna/async/DummyNeuron.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | class DummyNeuron(override val id: String, override val netId: String)
4 | extends Neuron(id, netId, 0.0, 0) {
5 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/logger/HTMLLogOutput.scala:
--------------------------------------------------------------------------------
1 | package anna.logger
2 |
3 | class HTMLLogOutput(override val id: String) extends StringLogOutput(id){
4 | override def println(str: String) = super.println(str + "
")
5 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/logger/StdoutLogOutput.scala:
--------------------------------------------------------------------------------
1 | package anna.logger
2 |
3 | class StdoutLogOutput extends LogOutput {
4 | override def id = "stdout"
5 | override def println(x: String) = Console.println(x)
6 | override def log = ""
7 | override def close(){}
8 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/logger/LogOutput.scala:
--------------------------------------------------------------------------------
1 | package anna.logger
2 |
3 | import java.util.Calendar
4 |
5 | trait LogOutput {
6 | protected def timeTag = Calendar.getInstance().toString()
7 |
8 | def log: String
9 |
10 | def println(str: String): Unit
11 |
12 | def close(): Unit
13 |
14 | def id: String
15 | }
--------------------------------------------------------------------------------
/src/main/resources/application.conf:
--------------------------------------------------------------------------------
1 | context {
2 | // system defaults
3 | awaitTimeout = 5
4 | maxRunIterations = 100
5 |
6 | // neuron defaults
7 | neuronDefaults {
8 | defaultThreshold = 0.25
9 | defaultWeight = "1.0"
10 | defaultSilenceIterations = 1
11 | defaultIterationTime = 200
12 | }
13 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/logger/StringLogOutput.scala:
--------------------------------------------------------------------------------
1 | package anna.logger
2 |
3 | class StringLogOutput(val id: String) extends LogOutput {
4 | private val sb = StringBuilder.newBuilder
5 | override def println(x: String) = sb.append(x).append('\n')
6 | override def log = sb.toString
7 | override def close() = sb.setLength(0)
8 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/logger/SystemLogOutput.scala:
--------------------------------------------------------------------------------
1 | package anna.logger
2 |
3 | class SystemLogOutput extends LogOutput {
4 | override def log = ""
5 |
6 | override def println(str: String) = System.console().printf(str+'\n')
7 |
8 | override def close(){
9 | // nothing to do here
10 | }
11 |
12 | override def id = "stdout"
13 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/logger/WhereAmICall.scala:
--------------------------------------------------------------------------------
1 | package anna.logger
2 |
3 | import java.io.{PrintWriter, StringWriter}
4 |
5 | class WhereAmICall extends IllegalArgumentException("Where Am I?")
6 |
7 | object WhereAmICall{
8 | def whereAmI = {
9 | val aThrowable = new WhereAmICall()
10 | val result = new StringWriter()
11 | aThrowable.printStackTrace(new PrintWriter(result))
12 | result.toString()
13 | }
14 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.cache
2 | .cache
3 | logs
4 | project/project
5 | project/target
6 | target
7 | tmp
8 | .history
9 | dist
10 | /.idea
11 | /*.iml
12 | /out
13 | /.idea_modules
14 | /.classpath
15 | /.project
16 | /RUNNING_PID
17 | /.settings
18 | *.class
19 | /bin
20 | .target/*
21 | *.diff
22 | *.log
23 | *.sc
24 | .worksheet*
25 | projectFilesBackup/*
26 | src/main/resources/evolution/*
27 | contextMatrixTest*
28 | .cache-main
29 | .cache-tests
30 | doc/*.odt
--------------------------------------------------------------------------------
/src/main/scala/anna/logger/ListLogOutput.scala:
--------------------------------------------------------------------------------
1 | package anna.logger
2 |
3 | /**
4 | * Created by gorywoda on 07.05.15.
5 | */
6 |
7 | import scala.collection.mutable
8 |
9 | class ListLogOutput(val id: String) extends LogOutput {
10 | private val _list = mutable.ListBuffer[String]()
11 | override def println(x: String) = _list += x
12 | override def log = _list.mkString("\n")
13 | override def close() = _list.clear()
14 |
15 | def list = _list.toList
16 | }
17 |
--------------------------------------------------------------------------------
/doc/docker-install.txt:
--------------------------------------------------------------------------------
1 | This is a short instruction on how to install ANNA with docker. No Dockerfile yet.
2 |
3 | From the shell:
4 | docker pull ubuntu
5 | docker run -i -t [image id] /bin/bash
6 |
7 | From docker (as root):
8 | echo "deb https://dl.bintray.com/sbt/debian /" | tee -a /etc/apt/sources.list.d/sbt.list
9 | apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 642AC823
10 | apt-get update
11 | apt-get install apt-transport-https
12 | apt-get update
13 | apt-get install default-jdk
14 | apt-get install sbt
15 | git clone https://github.com/makingthematrix/ann.git
16 | cd ann
17 | sbt compile
18 | sbt console
19 |
--------------------------------------------------------------------------------
/src/main/scala/anna/utils/IntRange.scala:
--------------------------------------------------------------------------------
1 | package anna.utils
2 |
3 | import anna.utils.Utils.formats
4 | import org.json4s.native.Serialization.{read, writePretty}
5 |
6 | /**
7 | * Created by gorywoda on 12.01.15.
8 | */
9 |
10 | case class IntRange(start: Int, end: Int) {
11 | def choose(x: Double) = math.round(x *(end - r.start - 1) + r.start).toInt
12 | def r = start to end
13 | def contains(x: Int) = r.contains(x)
14 | def toJson = writePretty(this)
15 | }
16 |
17 | object IntRange {
18 | implicit def fromRange(r: Range):IntRange = IntRange(r.start, r.end)
19 | def fromJson(str: String) = read[IntRange](str)
20 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/data/SynapseData.scala:
--------------------------------------------------------------------------------
1 | package anna.data
2 |
3 | import anna.utils.Utils.formats
4 | import org.json4s.native.Serialization.{read, writePretty}
5 |
6 | case class SynapseData(neuronId: String, weight: SynapseTrait){
7 | def withId(neuronId: String) = SynapseData(neuronId, weight)
8 | def withWeight(weight: SynapseTrait) = SynapseData(neuronId, weight)
9 |
10 | def toJson = writePretty(this)
11 | }
12 |
13 | object SynapseData {
14 | def apply(neuronId: String, weight: Double):SynapseData = SynapseData(neuronId, SynapseWeight(weight))
15 |
16 | def fromJson(jsonStr: String) = read[SynapseData](jsonStr)
17 |
18 | implicit def fromDouble(weight: Double):SynapseWeight = SynapseWeight(weight)
19 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/async/NeuronType.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.utils.Utils.formats
4 | import org.json4s.native.Serialization.{read, writePretty}
5 |
6 | sealed trait NeuronType extends Any
7 |
8 | case class NeuronTypeStandard() extends NeuronType {
9 | def toJson = writePretty(this)
10 | }
11 |
12 | case class NeuronTypeDummy() extends NeuronType {
13 | def toJson = writePretty(this)
14 | }
15 |
16 | case class NeuronTypeSilencing() extends NeuronType {
17 | def toJson = writePretty(this)
18 | }
19 |
20 | object NeuronType {
21 | def parse(str: String) = str match {
22 | case "STANDARD" => NeuronTypeStandard()
23 | case "DUMMY" => NeuronTypeDummy()
24 | case "SILENCING" => NeuronTypeSilencing()
25 | }
26 |
27 | def fromJson(str: String) = read[NeuronType](str)
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/src/main/scala/anna/async/SilencingNeuron.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.async.Messages._
4 | import anna.data.Wake
5 | import anna.logger.LOG
6 |
7 | class SilencingNeuron(override val id: String, override val netId: String)
8 | extends Neuron(id, netId, 0.0, 0) {
9 | private def sendRequest() = {
10 | synapses.foreach( s => s.weight match {
11 | case Wake() =>
12 | LOG += s"sending WakeUpRequest to ${s.dest.id}"
13 | s.dest.requestWakeUp()
14 | case _ =>
15 | LOG += s"sending SilenceRequest to ${s.dest.id}"
16 | s.dest.requestSilence()
17 | })
18 | triggerSilenceRequested()
19 | }
20 |
21 | override val activeBehaviour: Receive = {
22 | case Signal(s, id) => sendRequest()
23 | case SilenceRequest => sendRequest()
24 | case WakeRequest => sendRequest()
25 | }
26 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/utils/RandomNumber.scala:
--------------------------------------------------------------------------------
1 | package anna.utils
2 |
3 | import scala.util.Random
4 |
5 | /**
6 | * Created by gorywoda on 12.01.15.
7 | */
8 | object RandomNumber {
9 | private var _rand: Option[Random] = None
10 | private def rand() = _rand match {
11 | case None =>
12 | val r = new Random()
13 | _rand = Some(r)
14 | r
15 | case Some(r) => r
16 | }
17 |
18 | def apply():Double = rand().nextDouble()
19 | def apply(start:Int, end: Int):Int = apply(IntRange(start, end))
20 | def apply(range: IntRange):Int = if(range.start == range.end) range.start else range.choose(apply())
21 | def apply(range: DoubleRange):Double = if(range.from == range.to) range.from else range.choose(apply())
22 | def apply(end: Int):Int = apply(0, end)
23 | def apply[T](it: Iterable[T]):T = it.drop(apply(it.size)).head
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/scala/anna/data/SynapseTrait.scala:
--------------------------------------------------------------------------------
1 | package anna.data
2 |
3 | import anna.utils.DoubleRange._
4 | import anna.utils.Utils.formats
5 | import org.json4s.native.Serialization.{read, writePretty}
6 |
7 | /**
8 | * Created by gorywoda on 05.05.15.
9 | */
10 | sealed trait SynapseTrait extends Any
11 |
12 | case class SynapseWeight(weight: Double) extends SynapseTrait {
13 | def check = assert((-1.0<=>1.0).contains(weight))
14 | def toJson = writePretty(this)
15 | }
16 |
17 | case class Silence() extends SynapseTrait {
18 | def toJson = writePretty(this)
19 | }
20 |
21 | case class Wake() extends SynapseTrait {
22 | def toJson = writePretty(this)
23 | }
24 |
25 | object SynapseTrait {
26 | def apply(str: String) = str match {
27 | case "Silence" => Silence()
28 | case "Wake" => Wake()
29 | case str => SynapseWeight(str.toDouble)
30 | }
31 |
32 | def fromJson(str: String) = read[SynapseTrait](str)
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/scala/anna/async/Synapse.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.async.Messages._
4 | import anna.data.{Silence, SynapseTrait, SynapseWeight, Wake}
5 |
6 | class Synapse(val dest: NeuronRef, val weight: SynapseTrait){
7 |
8 | private def msg(signal: Double, senderId: String) = weight match {
9 | case Silence() => SilenceRequest
10 | case Wake() => WakeRequest
11 | case w: SynapseWeight => Signal(signal * w.weight, senderId)
12 | }
13 |
14 | def send(signal: Double, senderId: String) = {
15 | dest ! msg(signal, senderId)
16 | }
17 |
18 | override def toString() = s"Synapse(${dest.id}, $weight)"
19 |
20 | def info = SynapseInfo(dest.id, weight)
21 | }
22 |
23 | object Synapse{
24 | def apply(dest: NeuronRef, weight: SynapseTrait):Synapse = new Synapse(dest, weight)
25 | def apply(dest: NeuronRef, weight: Double):Synapse = apply(dest, SynapseWeight(weight))
26 | def apply(dest: NeuronRef):Synapse = apply(dest, 1.0)
27 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/async/SchedulerBuffer.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import akka.actor.{ActorContext, Cancellable}
4 |
5 | import scala.collection.mutable
6 | import scala.concurrent.ExecutionContext.Implicits.global
7 | import scala.concurrent.duration._
8 |
9 | /**
10 | * Created by gorywoda on 25.06.15.
11 | */
12 | class SchedulerBuffer(context : ActorContext){
13 | private val map = mutable.Map[Long,Cancellable]()
14 |
15 | def schedule(delay : FiniteDuration)(f : => scala.Unit) = {
16 | refresh()
17 | val c = context.system.scheduler.scheduleOnce(delay){
18 | f
19 | refresh()
20 | }
21 | val timestamp = System.currentTimeMillis() + delay.toMillis
22 | map += (timestamp -> c)
23 | timestamp
24 | }
25 |
26 | def unschedule(timestamp: Long) = synchronized {
27 | if(map.contains(timestamp)){
28 | val c = map(timestamp)
29 | if(!c.isCancelled) c.cancel()
30 | map -= timestamp
31 | }
32 | }
33 |
34 | def refresh() = synchronized {
35 | val t = System.currentTimeMillis()
36 | map.keys.filter(_ < t).foreach( unschedule )
37 | }
38 |
39 | def clear() = {
40 | map.values.foreach( c => if(!c.isCancelled) c.cancel())
41 | map.clear()
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/scala/anna/logger/FileLogOutput.scala:
--------------------------------------------------------------------------------
1 | package anna.logger
2 |
3 | import java.io.{BufferedWriter, File, FileWriter, IOException}
4 |
5 | class FileLogOutput(val fileName: String) extends LogOutput {
6 | private var writer:Option[BufferedWriter] = None
7 |
8 | private def newWriter = {
9 | val file = new File(fileName)
10 | if (!file.exists()) {
11 | file.createNewFile()
12 | file.setReadable(true)
13 | file.setWritable(true)
14 | }
15 | new BufferedWriter(new FileWriter(file, true))
16 | }
17 |
18 | private def getWriter = writer match {
19 | case Some(w) => w
20 | case None => {
21 | val w = newWriter
22 | writer = Some(w)
23 | w
24 | }
25 | }
26 |
27 | override def println(str: String) = try {
28 | val w = getWriter
29 | w.write(str)
30 | w.newLine()
31 | w.flush()
32 | } catch {
33 | case ex: IOException => ex.printStackTrace()
34 | }
35 |
36 | override def log: String = ""
37 |
38 | override def close(): Unit = writer match {
39 | case Some(w) => try {
40 | w.close()
41 | writer = None
42 | } catch {
43 | case ex: IOException => ex.printStackTrace()
44 | }
45 | case None =>
46 | }
47 |
48 | override def id: String = fileName
49 |
50 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/utils/DoubleRange.scala:
--------------------------------------------------------------------------------
1 | package anna.utils
2 |
3 | import anna.utils.Utils.formats
4 | import org.json4s.native.Serialization.{read, writePretty}
5 |
6 | import scala.annotation.tailrec
7 |
8 | case class RichD(val d: Double) extends AnyVal {
9 | def <=>(other: Double) = DoubleRange(d, other)
10 | }
11 |
12 | case class DoubleRange(from: Double, to: Double){
13 | def iterator(resolution: Int) = (0 to resolution).map{ x => choose(x.toDouble/resolution) }.view.iterator
14 |
15 | @tailrec
16 | final def choose(x: Double, withoutValues: List[Double] = Nil):Double = {
17 | val result = x*(to-from)+from
18 | if(withoutValues.contains(result)) choose(x, withoutValues) else result
19 | }
20 | def choose(x: Double, withoutValue: Double):Double = choose(x, List(withoutValue))
21 |
22 | def contains(x: Double):Boolean = x >= from && x <= to
23 | def contains(r: DoubleRange):Boolean = contains(r.from) && contains(r.to)
24 | def intersects(r: DoubleRange) = !contains(r) && !r.contains(this)
25 | def univalue: Option[Double] = if(from == to) Some(from) else None
26 |
27 | def toJson = writePretty(this)
28 | }
29 |
30 | object DoubleRange {
31 | implicit def fromDouble(d: Double) = new RichD(d)
32 | def fromJson(jsonStr: String) = read[DoubleRange](jsonStr)
33 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/data/NeuronData.scala:
--------------------------------------------------------------------------------
1 | package anna.data
2 |
3 | import anna.Context
4 | import anna.async._
5 | import anna.utils.Utils.formats
6 | import org.json4s.native.Serialization.{read, writePretty}
7 |
8 | case class NeuronData(id: String,
9 | threshold: Double,
10 | silenceIterations: Int,
11 | synapses: List[SynapseData],
12 | neuronType: NeuronType){
13 | def withId(id: String) = copy(id = id)
14 | def withThreshold(threshold: Double) = copy(threshold = threshold)
15 | def withSilenceIterations(silenceIterations: Int) = copy(silenceIterations = silenceIterations)
16 | def withSynapses(synapses: List[SynapseData]) = copy(synapses = synapses)
17 | def withoutSynapses = withSynapses(Nil)
18 | def withNeuronType(neuronType: NeuronType) = copy(neuronType = neuronType)
19 |
20 | def toJson = writePretty(this)
21 |
22 | def isConnectedTo(id: String) = synapses.find(_.neuronId == id) != None
23 | }
24 |
25 | object NeuronData {
26 | def apply(id: String, threshold: Double, silenceIterations: Int, synapses: List[SynapseData]):NeuronData
27 | = apply(id, threshold, silenceIterations, synapses, NeuronTypeStandard())
28 |
29 | def apply(id: String, silenceIterations: Int):NeuronData
30 | = apply(id, 0.0, silenceIterations, Nil, NeuronTypeDummy())
31 |
32 | def apply(id: String):NeuronData = apply(id, 0.0, Context().silenceIterations, Nil, NeuronTypeSilencing())
33 |
34 | def fromJson(jsonStr: String) = read[NeuronData](jsonStr)
35 | }
--------------------------------------------------------------------------------
/src/test/scala/anna/async/DotNetSuite.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.async.NetBuilderOps._
4 | import anna.logger.LOG
5 | import anna.logger.LOG.debug
6 | import org.junit.Assert._
7 | import org.junit.Test
8 | import org.scalatest.junit.JUnitSuite
9 |
10 | class DotNetSuite extends JUnitSuite {
11 | val s = "1,0,0,0,1,0,0,0,1,0,0,0"
12 | val o = "1,1,0,0,1,1,0,0,1,1,0,0"
13 |
14 | private def dotNet3 = {
15 | NetBuilder().addInput("in")
16 | // dots
17 | .use("in").chain("mi11", 1.0, 0.0, 2).silence("mi11")
18 | .chain("mi12",1.0,0.0).loop("loop1",1.0,0.0,1.0)
19 | .chain("dot",0.6/2.0,0.6).silence("mi12").silence("loop1").silence("dot")
20 | .build()
21 | }
22 |
23 | @Test def shouldHaveDotInterval3() = {
24 | val netWrapper = dotNet3
25 | debug("------------")
26 |
27 | var dots = 0
28 | netWrapper += s
29 |
30 | LOG.timer()
31 | netWrapper.addAfterFire("in"){ debug("received input") }
32 | netWrapper.addAfterFire("dot"){ debug("Dot!"); dots += 1 }
33 |
34 | val interval = netWrapper.iterateUntilCalm()
35 | debug(s"interval: $interval, dots: $dots")
36 | assertEquals(3, dots)
37 |
38 | dots = 0
39 | netWrapper += o
40 |
41 | // without the other part of the network which would recognize the "line" signals,
42 | // lines should also be recognized as dots - maybe the additional '1's mean only that the signal is noised
43 |
44 | val interval2 = netWrapper.iterateUntilCalm()
45 | debug(s"interval: $interval, dots: $dots")
46 | assertEquals(3, dots)
47 |
48 | netWrapper.shutdown()
49 | }
50 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/blocks/SignalSum.scala:
--------------------------------------------------------------------------------
1 | package anna.blocks
2 |
3 | import anna.async.NetBuilder
4 | import anna.async.NetBuilderOps._
5 |
6 | /**
7 | * Created by gorywoda on 6/19/16.
8 | */
9 | case class SignalSum(name: String, requiredSignalsNumber: Int){
10 | import SignalSum.middleThreshold
11 |
12 | lazy val data = {
13 | val builder = NetBuilder()
14 | chain(builder)
15 | builder.data
16 | }
17 |
18 | def chain(builder: NetBuilder, inputWeight: Double = 1.0, inputThreshold: Double = 0.0) = {
19 | val middleSynapseWeigth = middleThreshold/requiredSignalsNumber
20 |
21 | if(builder.isCurrent) builder.chain(inputId, inputWeight, inputThreshold, 0)
22 | else builder.addMiddle(id=inputId, threshold=0.0, silenceIterations = 0)
23 |
24 | builder.use(inputId).chain(outputId, middleSynapseWeigth, middleThreshold, 0)
25 | .addSilencingNeuron(silencingId).silence(inputId).silence(outputId)
26 | .use(outputId)
27 | }
28 |
29 | val inputId = SignalSum.inputId(name)
30 | val outputId = SignalSum.outputId(name)
31 | val silencingId = SignalSum.silencingId(name)
32 | }
33 |
34 | object SignalSum {
35 | val middleThreshold = 0.9
36 |
37 | val blockNamePrefix = "SignalSum"
38 | val nameRegex = s""".*${blockNamePrefix}#([0-9]+)#.*""".r
39 | val neuronsInBlock = 3
40 |
41 | def nextName() = s"${blockNamePrefix}#${firstFreeId}#"
42 |
43 | private var firstFreeId = 1
44 |
45 | def apply(requiredSignalsNumber: Int):SignalSum = {
46 | val newName = nextName()
47 | firstFreeId += 1
48 | SignalSum(newName, requiredSignalsNumber)
49 | }
50 |
51 | def inputId(name: String) = s"${name}1"
52 | def outputId(name: String) = s"${name}2"
53 | def silencingId(name: String) = s"${name}s"
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/scala/anna/blocks/Sequencer.scala:
--------------------------------------------------------------------------------
1 | package anna.blocks
2 |
3 | import anna.async.{NetBuilder, Neuron}
4 | import anna.async.NetBuilderOps._
5 |
6 | case class Sequencer(name: String) {
7 | def chain(builder: NetBuilder,
8 | input1Id: String, input2Id: String,
9 | input1Weight: Double = 1.0, input1Threshold: Double = 0.0,
10 | input2Weight: Double = 1.0, input2Threshold: Double = 0.0
11 | ) =
12 | // oh, screw it. Let's assume input neurons are already there.
13 | builder.use(input1Id).chain(this.input1Id, input1Weight, input1Threshold, Neuron.SilenceForever).silence(this.input1Id)
14 | .use(input2Id).chain(this.input2Id, input2Weight, input2Threshold, Neuron.SilenceForever).initSilent().silence(this.input2Id)
15 | .use(this.input1Id).wake(this.input2Id)
16 | .use(this.input2Id).wake(this.input1Id)
17 | .addSilencingNeuron(silencingId).wake(this.input1Id).silence(this.input2Id)
18 | .use(this.input2Id)
19 |
20 | val input1Id = Sequencer.input1Id(name)
21 | val input2Id = Sequencer.input2Id(name)
22 | val outputId = input2Id
23 | val silencingId = Sequencer.silencingId(name)
24 | }
25 |
26 | object Sequencer {
27 | val blockNamePrefix = "Sequencer"
28 | val nameRegex = s""".*${blockNamePrefix}#([0-9]+)#.*""".r
29 | val neuronsInBlock = 3
30 |
31 | def nextName() = s"${blockNamePrefix}#${firstFreeId}#"
32 |
33 | private var firstFreeId = 1
34 |
35 | def apply(requiredSignalsNumber: Int):SignalSum = {
36 | val newName = nextName()
37 | firstFreeId += 1
38 | SignalSum(newName, requiredSignalsNumber)
39 | }
40 |
41 | def input1Id(name: String) = s"${name}1"
42 | def input2Id(name: String) = s"${name}2"
43 | def outputId(name: String) = input2Id(name)
44 | def silencingId(name: String) = s"${name}s"
45 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/blocks/DelayGate.scala:
--------------------------------------------------------------------------------
1 | package anna.blocks
2 |
3 | import anna.async.NetBuilder
4 | import anna.async.NetBuilderOps._
5 |
6 | /**
7 | * Created by gorywoda on 1/31/16.
8 | */
9 | case class DelayGate(name: String, delay: Int){
10 | lazy val data = {
11 | val builder = NetBuilder()
12 | chain(builder)
13 | builder.data
14 | }
15 |
16 | def chain(builder: NetBuilder, inputWeight: Double = 1.0, inputThreshold: Double = 0.0) = {
17 | val feedbackWeight = DelayGate.middleThreshold / (delay + 1)
18 | if(builder.isCurrent) builder.chain(inputId, inputWeight, inputThreshold, delay)
19 | else builder.addMiddle(id=inputId, threshold=inputThreshold, silenceIterations=delay)
20 |
21 | builder.use(inputId).silence(inputId).chain(middleId, 1.0, 0.01).connect(middleId, 1.0)
22 | .chain(outputId, feedbackWeight, DelayGate.middleThreshold).silence(middleId)
23 | .addSilencingNeuron(silencingId).silence(inputId).silence(middleId).silence(outputId)
24 | .use(outputId) // always end chaining with setting the current neuron at the main output of the block
25 | }
26 |
27 | val inputId = DelayGate.inputId(name)
28 | val middleId = DelayGate.middleId(name)
29 | val outputId = DelayGate.outputId(name)
30 | val silencingId = DelayGate.silencingId(name)
31 | }
32 |
33 | object DelayGate {
34 | val blockNamePrefix = "DelayGate"
35 | val nameRegex = s""".*${blockNamePrefix}#([0-9]+)#.*""".r
36 | val neuronsInBlock = 4
37 |
38 | val middleThreshold = 0.9
39 |
40 | def nextName() = s"${blockNamePrefix}#${firstFreeId}#"
41 |
42 | private var firstFreeId = 1
43 |
44 | def apply(delay: Int):DelayGate = {
45 | val newName = nextName()
46 | firstFreeId += 1
47 | DelayGate(newName, delay)
48 | }
49 |
50 | def inputId(name: String) = s"${name}1"
51 | def middleId(name: String) = s"${name}2"
52 | def outputId(name: String) = s"${name}3"
53 | def silencingId(name: String) = s"${name}s"
54 |
55 | }
--------------------------------------------------------------------------------
/src/test/scala/anna/blocks/DelayGateSuite.scala:
--------------------------------------------------------------------------------
1 | package anna.blocks
2 |
3 | import anna.async.NetBuilder
4 | import anna.async.NetBuilderOps._
5 | import anna.logger.LOG
6 | import org.junit.Assert._
7 | import org.junit.Test
8 | import org.scalatest.junit.JUnitSuite
9 |
10 |
11 | /**
12 | * Created by gorywoda on 1/31/16.
13 | */
14 | class DelayGateSuite extends JUnitSuite {
15 |
16 | private def delayGateWithOps(blockName: String, delay: Int) =
17 | NetBuilder().addInput("in").delayGate(blockName, delay).build()
18 |
19 | private def assertDelayGateWithOps(delay: Int) = {
20 | val netWrapper = delayGateWithOps("delayGate",delay)
21 | var iteration = 0
22 | LOG.allow("delayGate3")
23 | netWrapper.addAfterFire("delayGate3"){ iteration = netWrapper.iteration }
24 | netWrapper.iterateUntilCalm("1")
25 |
26 | assertEquals(delay, iteration-1)
27 |
28 | netWrapper.shutdown()
29 | }
30 |
31 | @Test def shouldDelayGateWithOps(): Unit = {
32 | assertDelayGateWithOps(3)
33 | assertDelayGateWithOps(2)
34 | assertDelayGateWithOps(1)
35 | assertDelayGateWithOps(0)
36 | }
37 |
38 | private def delayGateWithBlock(delay: Int) = {
39 | val builder = NetBuilder()
40 | builder.addInput("in")
41 |
42 | val block = DelayGate(delay)
43 | block.chain(builder)
44 | (builder.build(), block)
45 | }
46 |
47 | private def shouldFireWithBlock(expectedDelay: Int) = {
48 | val (netWrapper, block) = delayGateWithBlock(expectedDelay)
49 |
50 | var fired = false
51 | var iteration = 0
52 | netWrapper.addAfterFire(block.outputId){
53 | LOG.debug(s"iteration: ${netWrapper.iteration}, ${block.outputId} fired")
54 | iteration = netWrapper.iteration
55 | fired = true
56 | }
57 | netWrapper.iterateUntilCalm("1")
58 |
59 | assertTrue(fired)
60 | assertEquals(expectedDelay, iteration-1)
61 |
62 | netWrapper.shutdown()
63 | }
64 |
65 | @Test def shouldFireWithBlock(): Unit = {
66 | for(i <- 0 to 5){
67 | shouldFireWithBlock(i)
68 | }
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/test/scala/anna/async/DelaySuite.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.Context
4 | import anna.async.NetBuilderOps._
5 | //import anna.async.TestUtils._
6 | import anna.logger.LOG
7 | import org.junit.Assert._
8 | import org.junit.Test
9 | import org.scalatest.junit.JUnitSuite
10 |
11 | import scala.collection.mutable
12 |
13 | class DelaySuite extends JUnitSuite {
14 |
15 | @Test def shouldGiveConstantOutput(){
16 | val netWrapper = NetBuilder().addInput("in1").chain("mi1",1.0).chain("out1",1.0,0.75).build("net")
17 |
18 | netWrapper += "1,1,1,1,1,1"
19 |
20 | LOG.timer()
21 | val list = mutable.ListBuffer[Long]()
22 | netWrapper.addAfterFire("out1","fired"){ list += LOG.time }
23 |
24 | netWrapper.iterateUntilCalm()
25 |
26 | val tolerance = Context().iterationTime
27 | //assertEqualsWithTolerance(produceSeq(6, tolerance, Context().iterationTime), list, tolerance)
28 |
29 | netWrapper.shutdown()
30 | }
31 |
32 | @Test def shouldCreateOscillatorWithMethod2() {
33 | val netWrapper = NetBuilder().addInput("in").chain("mi1", 1.0).oscillator().chain("out1", 1.0, 0.75).build()
34 |
35 | netWrapper += "1,1,1,1,1,1"
36 |
37 | val list = mutable.ListBuffer[Int]()
38 | netWrapper.addAfterFire("out1", "fired"){
39 | list += netWrapper.iteration
40 | }
41 |
42 | netWrapper.iterateUntilCalm()
43 |
44 | assertEquals(List(1, 3, 5), list.toList)
45 |
46 | netWrapper.shutdown()
47 | }
48 |
49 | @Test def shouldCreateOscillator2(){
50 | val netWrapper = NetBuilder().addInput("in1").chain("mi1",1.0).loop("osc",1.0,0.5,-1.0).chain("out1",1.0,0.75)
51 | .use("osc").chain("mi2",1.0).chain("out2",1.0,0.75)
52 | .build()
53 |
54 | netWrapper += "1,1,1,1,1,1"
55 |
56 | val sb = StringBuilder.newBuilder
57 | netWrapper.addAfterFire("out1","fired 1"){ sb.append('1') }
58 | netWrapper.addAfterFire("out1","fired 0"){ sb.append('0') }
59 |
60 | netWrapper.iterateUntilCalm()
61 |
62 | assertEquals("101010", sb.toString)
63 |
64 | netWrapper.shutdown()
65 | }
66 | }
--------------------------------------------------------------------------------
/src/test/scala/anna/data/JsonSuite.scala:
--------------------------------------------------------------------------------
1 | package anna.data
2 |
3 | import anna.logger.LOG
4 | import org.junit.Assert._
5 | import org.junit.{Before, Test}
6 | import org.scalatest.junit.JUnitSuite
7 |
8 | /**
9 | * Created by gorywoda on 27.12.14.
10 | */
11 | class JsonSuite extends JUnitSuite {
12 | @Before def before(): Unit = {
13 | LOG.addLogToStdout()
14 | }
15 |
16 | @Test def shouldMakeJsonFromSynapseData() = {
17 | val expectedJson1 =
18 | """
19 | |{
20 | | "neuronId":"id1",
21 | | "weight":{
22 | | "jsonClass":"SynapseWeight",
23 | | "weight":1.0
24 | | }
25 | |}
26 | """.stripMargin.trim
27 | val data1 = SynapseData("id1",1.0)
28 | assertEquals(expectedJson1, data1.toJson)
29 |
30 | val expectedJson2 =
31 | """
32 | |{
33 | | "neuronId":"id2",
34 | | "weight":{
35 | | "jsonClass":"Silence"
36 | | }
37 | |}
38 | """.stripMargin.trim
39 | val data2 = SynapseData("id2",Silence())
40 | assertEquals(expectedJson2, data2.toJson)
41 | }
42 |
43 | @Test def shouldMakeSynapseDataFromJson() = {
44 | val json1 =
45 | """
46 | |{
47 | | "neuronId":"id1",
48 | | "weight":{
49 | | "jsonClass":"SynapseWeight",
50 | | "weight":1.0
51 | | }
52 | |}
53 | """.stripMargin.trim
54 | val data1 = SynapseData.fromJson(json1)
55 | assertEquals(SynapseData("id1",1.0), data1)
56 |
57 | val json2 =
58 | """
59 | |{
60 | | "neuronId":"id2",
61 | | "weight":{
62 | | "jsonClass":"Silence"
63 | | }
64 | |}
65 | """.stripMargin.trim
66 | val data2 = SynapseData.fromJson(json2)
67 | assertEquals(SynapseData("id2",Silence()), data2)
68 |
69 | val d4 = SynapseData("id1",1.0)
70 | val json4 = d4.toJson
71 | assertEquals(d4, SynapseData.fromJson(json4))
72 | }
73 |
74 | @Test def shouldMakeJsonFromNeuronData() = {
75 | val n1 = NeuronData("id1",0.0, 1, Nil)
76 | val json = n1.toJson
77 | val n2 = NeuronData.fromJson(json)
78 | assertEquals(n1, n2)
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/scala/anna/async/NeuronTriggers.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.async.NeuronTriggers.Trigger
4 | import scala.collection.mutable
5 |
6 | import anna.logger.LOG
7 |
8 | trait NeuronTriggers {
9 | private val afterFire = mutable.Map[String, Trigger]()
10 | private val signalIgnored = mutable.Map[String, Trigger]()
11 | private val silenceRequested = mutable.Map[String, Trigger]()
12 |
13 | private def add(id: String, f: Trigger, triggers: mutable.Map[String, Trigger]) =
14 | if(triggers.contains(id)) throw new IllegalArgumentException(s"There was already registered a trigger $id")
15 | else { triggers.put(id, f) }
16 |
17 | private def is(id: String, triggers: mutable.Map[String, Trigger]) = triggers.contains(id)
18 | private def remove(id: String, triggers: mutable.Map[String, Trigger]) = triggers.remove(id)
19 | private def clear(triggers: mutable.Map[String, Trigger]) = triggers.clear()
20 | private def trigger(triggers: mutable.Map[String, Trigger]) = {
21 | triggers.values.foreach( f => f() )
22 | }
23 |
24 | def addAfterFire(id: String, f: Trigger) = add(id, f, afterFire)
25 | def isAfterFire(id: String) = is(id, afterFire)
26 | def removeAfterFire(id: String) = remove(id, afterFire)
27 | def clearAfterFire() = clear(afterFire)
28 | def triggerAfterFire() = trigger(afterFire)
29 |
30 | def addSignalIgnored(id: String, f: Trigger) = add(id, f, signalIgnored)
31 | def isSignalIgnored(id: String) = is(id, signalIgnored)
32 | def removeSignalIgnored(id: String) = remove(id, signalIgnored)
33 | def clearSignalIgnored() = clear(signalIgnored)
34 | def triggerSignalIgnored() = trigger(signalIgnored)
35 |
36 | def addSilenceRequested(id: String, f: Trigger) = add(id, f, silenceRequested)
37 | def isSilenceRequested(id: String) = is(id, silenceRequested)
38 | def removeSilenceRequested(id: String) = remove(id, silenceRequested)
39 | def clearSilenceRequested() = clear(silenceRequested)
40 | def triggerSilenceRequested() = trigger(silenceRequested)
41 |
42 | def removeAllTriggers() = {
43 | afterFire.clear()
44 | signalIgnored.clear()
45 | silenceRequested.clear()
46 | }
47 | }
48 |
49 | object NeuronTriggers {
50 | type Trigger = () => Any
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/scala/anna/blocks/SignalSumSuite.scala:
--------------------------------------------------------------------------------
1 | package anna.blocks
2 |
3 | import anna.async.NetBuilderOps._
4 | import anna.async.{NetBuilder, NetWrapper}
5 | import anna.logger.LOG
6 | import org.junit.Assert._
7 | import org.junit.Test
8 | import org.scalatest.junit.JUnitSuite
9 |
10 | /**
11 | * Created by gorywoda on 6/19/16.
12 | */
13 | class SignalSumSuite extends JUnitSuite {
14 |
15 | private def signalSumWithOps(blockName: String, requiredSignals: Int) =
16 | NetBuilder().addInput("in").signalSum(blockName, requiredSignals).build()
17 |
18 | private def assertFiredAfterRequiredSignals(netWrapper: NetWrapper, outNeuronId: String, requiredSignals: Int) = {
19 | var fired = false
20 | netWrapper.addAfterFire(outNeuronId){ fired = true }
21 |
22 | val inputVector = List.fill(requiredSignals)("1").mkString(",")
23 | LOG.debug("inputVector: " + inputVector)
24 | netWrapper.iterateUntilCalm(inputVector)
25 | assertTrue(fired)
26 |
27 | if(requiredSignals > 1) {
28 | fired = false
29 |
30 | val inputVectorMinus1 = List.fill(requiredSignals - 1)("1").mkString(",")
31 | LOG.debug("inputVectorMinus1: " + inputVectorMinus1)
32 | netWrapper.iterateUntilCalm(inputVectorMinus1)
33 | assertFalse(fired)
34 | }
35 | }
36 |
37 | @Test def shouldSignalSumWithOps(): Unit ={
38 | val netWrapper = signalSumWithOps("SignalSum", 3)
39 | assertFiredAfterRequiredSignals(netWrapper, "SignalSum2", 3)
40 | netWrapper.shutdown()
41 | }
42 |
43 | private def signalSumWithBlock(requiredSignals: Int) = {
44 | val builder = NetBuilder()
45 | builder.addInput("in")
46 |
47 | val block = SignalSum(requiredSignals)
48 | block.chain(builder)
49 | (builder.build(), block)
50 | }
51 |
52 | private def shouldFireAfterRequiredSignalsBlock(requiredSignals: Int) = {
53 | val (netWrapper, block) = signalSumWithBlock(requiredSignals)
54 | assertFiredAfterRequiredSignals(netWrapper, block.outputId, requiredSignals)
55 | netWrapper.shutdown()
56 | }
57 |
58 | @Test def shouldFireWithBlock(): Unit = {
59 | shouldFireAfterRequiredSignalsBlock(3)
60 | shouldFireAfterRequiredSignalsBlock(2)
61 | shouldFireAfterRequiredSignalsBlock(1)
62 | }
63 |
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/test/scala/anna/data/NetDataSuite.scala:
--------------------------------------------------------------------------------
1 | package anna.data
2 |
3 | import anna.async.NetBuilderOps._
4 | import anna.async.{NetBuilder, NeuronTypeDummy, NeuronTypeStandard}
5 | import anna.logger.LOG
6 | import org.junit.Assert._
7 | import org.junit.{Before, Test}
8 | import org.scalatest.junit.JUnitSuite
9 |
10 | /**
11 | * Created by gorywoda on 03.01.15.
12 | */
13 | class NetDataSuite extends JUnitSuite {
14 | @Before def before(): Unit = {
15 | LOG.addLogToStdout()
16 | }
17 |
18 | @Test def shouldMakeNetDataFromJson() = {
19 | val s1 = SynapseData("id2",1.0)
20 | val n1 = NeuronData("id1",0.0, 1, List(s1), NeuronTypeDummy())
21 | val n2 = NeuronData("id2",0.0, 2, Nil)
22 | val netData = NetData("net",List(n1,n2),List("id1"))
23 |
24 | val json = netData.toJson
25 | assertEquals(netData, NetData.fromJson(json))
26 | }
27 |
28 | @Test def shouldMakeNetDataWithBuilder() = {
29 | val s1 = SynapseData("id2",1.0)
30 | val n1 = NeuronData("id1", 1).withSynapses(List(s1))
31 | val n2 = NeuronData("id2",0.0, 2, Nil)
32 | val netData = NetData("net",List(n1,n2),List("id1"))
33 |
34 | val builder = NetBuilder()
35 |
36 | builder.addInput("id1").chain("id2", 1.0, 0.0, 2)
37 |
38 | print("---- net data ----")
39 | print(netData.toJson)
40 | print("---- builder ----")
41 | print(builder.data.toJson)
42 |
43 | assertEquals(netData.toJson, builder.data.toJson)
44 | }
45 |
46 | @Test def shouldBuildNetWithData() = {
47 | val s1 = SynapseData("id2",1.0)
48 | val n1 = NeuronData("id1", 0.0, 1, List(s1), NeuronTypeDummy())
49 | val n2 = NeuronData("id2", 0.0, 2, Nil, NeuronTypeStandard())
50 | val netData = NetData("net",List(n1,n2),List("id1"))
51 |
52 | val builder = NetBuilder()
53 | builder.set(netData)
54 |
55 | val netWrapper = builder.build("in")
56 | val neurons = netWrapper.net.neurons
57 | assertEquals(2, neurons.size)
58 | assertEquals(List("id1","id2"), neurons.map(_.id).sorted)
59 |
60 | val sb = StringBuilder.newBuilder
61 | netWrapper.addAfterFire("id2"){ sb.append(".") }
62 |
63 | netWrapper.iterateUntilCalm("1,1,1")
64 |
65 | assertEquals("...",sb.toString)
66 |
67 | netWrapper.shutdown()
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/test/scala/anna/async/NetSuite.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.Context
4 | import anna.async.Messages._
5 | import anna.utils.Utils.await
6 | import org.junit.Assert._
7 | import org.junit.Test
8 | import org.scalatest.junit.JUnitSuite
9 |
10 | class NetSuite extends JUnitSuite {
11 | val threshold = Context().threshold
12 | val weight = Context().weight
13 | val silenceIterations = Context().silenceIterations
14 | val timeout = Context().timeout
15 |
16 | @Test def shouldCreateNet(){
17 | val net = NetRef("net1")
18 |
19 | val msg = await[Msg](net.ref, GetId)
20 | assertEquals("net1",msg.str)
21 |
22 | net.shutdown()
23 | }
24 |
25 | @Test def shouldCreateNeurons(){
26 | val net = NetRef("net1")
27 |
28 | val n1 = net.createNeuron("id1", threshold, silenceIterations)
29 | val n2 = net.createNeuron("id2", threshold, silenceIterations)
30 |
31 | val msg = await[MsgNeurons](net, GetNeurons)
32 | val neurons = msg.neurons
33 | assertEquals(2, neurons.size)
34 |
35 | val ids = neurons.map{ _.id }
36 | assertTrue(ids.contains(n1.id))
37 | assertTrue(ids.contains(n2.id))
38 |
39 | net.shutdown()
40 | }
41 |
42 | @Test def shouldCreateNeuronsWithBuilder(){
43 | val builder = NetBuilder()
44 | builder.addMiddle("id1", threshold, silenceIterations)
45 | .addMiddle("id2", threshold, silenceIterations)
46 | val net = builder.build("net").net
47 |
48 | val neurons = net.neurons
49 | assertEquals(2, neurons.size)
50 | val ids = neurons.map{ _.id }
51 | assertTrue(ids.contains("id1"))
52 | assertTrue(ids.contains("id2"))
53 |
54 | net.shutdown()
55 | }
56 |
57 | @Test def shouldConnectNeuronsWithBuilder(){
58 | val builder = NetBuilder()
59 | builder.addMiddle("id1", threshold, silenceIterations)
60 | .chain("id2", weight, threshold, silenceIterations)
61 | val net = builder.build("net").net
62 |
63 | val neurons = net.neurons
64 | assertEquals(2, neurons.size)
65 |
66 | val n1Opt = neurons.find(_.id == "id1")
67 | assertTrue(n1Opt != None)
68 | val n1 = n1Opt.get
69 |
70 | val synapses = n1.getSynapses
71 | assertEquals(1, synapses.size)
72 |
73 | val nRef = synapses(0).dest
74 | assertEquals("id2", nRef.id)
75 |
76 | net.shutdown()
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/src/test/scala/anna/async/NeuronTriggersSuite.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.Context
4 | import anna.data.SynapseWeight
5 | import anna.logger.LOG
6 | import org.junit.Assert._
7 | import org.junit.Test
8 | import org.scalatest.junit.JUnitSuite
9 | import anna.async.NetBuilderOps._
10 |
11 | import scala.concurrent.{Await, Promise}
12 |
13 | /**
14 | * Created by gorywoda on 10/31/16.
15 | */
16 | class NeuronTriggersSuite extends JUnitSuite {
17 | val timeout = Context().timeout
18 |
19 | @Test def shouldTriggerAfterFireWithNetWrapper(){
20 | val wrapper = NetBuilder().addInput("in").chain("out", 1.0, 0.9, 0).build()
21 |
22 | val p = Promise[Boolean]()
23 | wrapper.addAfterFire("out", "MyTrigger"){
24 | LOG.debug("received after fire request")
25 | p.success(true)
26 | }
27 |
28 | LOG.allow("in", "out")
29 | wrapper.iterateUntilCalm("1")
30 |
31 | val afterFireReceived = Await.result(p.future, timeout.duration)
32 | assertTrue(afterFireReceived)
33 |
34 | wrapper.shutdown()
35 | }
36 |
37 | @Test def shouldTriggerAfterFireWithNeuron(){
38 | val net = NetRef("net1")
39 |
40 | val n1 = net.createNeuron("id1", 0.0, 0)
41 | val n2 = net.createNeuron("id2", 0.9, 0)
42 |
43 | n1.setSynapses(List(Synapse(n2, SynapseWeight(1.0))))
44 | net.setInputs(List(n1.id))
45 |
46 | val p = Promise[Boolean]()
47 |
48 | n2.addAfterFire("MyTrigger"){
49 | LOG.debug("received after fire in id2")
50 | p.success(true)
51 | }
52 |
53 | net.signal(List(1.0))
54 |
55 | val silenceRequestReceived = Await.result(p.future, timeout.duration)
56 | assertTrue(silenceRequestReceived)
57 |
58 | net.shutdown()
59 | }
60 |
61 | @Test def shouldTriggerAfterFireWithNet(){
62 | val net = NetRef("net1")
63 |
64 | val n1 = net.createNeuron("id1", 0.0, 0)
65 | val n2 = net.createNeuron("id2", 0.9, 0)
66 |
67 | n1.setSynapses(List(Synapse(n2, SynapseWeight(1.0))))
68 | net.setInputs(List(n1.id))
69 |
70 | val p = Promise[Boolean]()
71 |
72 | net.addAfterFire("id2", "MyTrigger"){
73 | LOG.debug("received after fire in id2")
74 | p.success(true)
75 | }
76 |
77 | LOG.allow("id1", "id2")
78 | net.signal(List(1.0))
79 |
80 | val silenceRequestReceived = Await.result(p.future, timeout.duration)
81 | assertTrue(silenceRequestReceived)
82 |
83 | net.shutdown()
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/test/scala/anna/async/LineNetSuite.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.Context
4 | import anna.async.NetBuilderOps._
5 | import anna.logger.LOG
6 | import anna.logger.LOG.debug
7 | import org.junit.Assert._
8 | import org.junit.{After, Before, Test}
9 | import org.scalatest.junit.JUnitSuite
10 |
11 | class LineNetSuite extends JUnitSuite {
12 | val s = "1,0,0,0,1,0,0,0,1,0,0,0"
13 | val o = "1,1,0,0,1,1,0,0,1,1,0,0"
14 |
15 | private def lineNet2 = {
16 | val netWrapper = NetBuilder()
17 | .addInput("in1")
18 | .chain("mi21",0.4,0.6).silence("mi21")
19 | .chain("out2",1.0)
20 | .build()
21 | debug("----------")
22 | val sb = StringBuilder.newBuilder
23 | netWrapper.addAfterFire("out2"){
24 | debug("Line!")
25 | sb.append('-')
26 | }
27 |
28 | (netWrapper, sb)
29 | }
30 |
31 | @Test def shouldLineThenNothing1(){
32 | val (netWrapper, sb) = lineNet2
33 |
34 | netWrapper += "1,1,0,0,0,0"
35 |
36 | netWrapper.iterateUntilCalm()
37 | assertEquals("-",sb.toString)
38 |
39 | netWrapper.shutdown()
40 | }
41 |
42 | @Test def shouldLine3Times(){
43 | val (netWrapper, sb) = lineNet2
44 |
45 | netWrapper += "1,1,0,1,1,0,1,1,0"
46 |
47 | netWrapper.iterateUntilCalm()
48 | assertEquals("---",sb.toString)
49 |
50 | netWrapper.shutdown()
51 | }
52 |
53 | private def lineNet3 =
54 | NetBuilder().addInput("in")
55 | .chain("mi21",0.5,0.55)
56 | .silence("mi21")
57 | .chain("line",1.0,0.0).silence("line")
58 | .build()
59 |
60 | @Test def shouldHaveLineInterval3() = {
61 | val netWrapper = lineNet3
62 | debug("------------")
63 | var lines = 0
64 | netWrapper.addAfterFire("in"){ debug("Incoming!") }
65 | netWrapper.addAfterFire("line"){ debug("Line!"); lines += 1; }
66 |
67 | netWrapper += o
68 |
69 | val interval = netWrapper.iterateUntilCalm()
70 | debug(s"interval: $interval, lines: $lines")
71 | assertEquals(3, lines)
72 |
73 | lines = 0
74 | netWrapper += s
75 | netWrapper.iterateUntilCalm()
76 | debug(s"lines: $lines")
77 | assertEquals(1, lines)
78 | // Without the other part of the network recognizing 'dots', two consecutive '1's, even with an interval
79 | // between them, should be recognized as a line. The interval may mean that the signal is noised.
80 |
81 | netWrapper.shutdown()
82 | }
83 |
84 |
85 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/async/NeuronRef.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import akka.actor.{ActorRef, actorRef2Scala}
4 | import anna.async.Messages._
5 | import anna.logger.LOG._
6 | import anna.utils.Utils.await
7 | import anna.async.NeuronTriggers.Trigger
8 |
9 | class NeuronRef(val id: String, val ref: ActorRef) {
10 | def info = await[NeuronInfo](ref, GetData)
11 | def getSynapses = await[MsgSynapses](ref, GetSynapses).synapses
12 | def setSynapses(synapses: Seq[Synapse]) = if(synapses.nonEmpty) ref ! SetSynapses(synapses)
13 |
14 | def requestSilence() = {
15 | debug(this, s"request silence for $id")
16 | ref ! SilenceRequest
17 | }
18 |
19 | def requestWakeUp() = {
20 | debug(this, s"request wake up for $id")
21 | ref ! WakeRequest
22 | }
23 |
24 | protected def calculateOutput = Double.NaN // we don't do that here
25 |
26 | def addAfterFire(triggerId: String)(f: => Any) = await[Answer](ref, AddAfterFireTrigger(triggerId, () => f)) match {
27 | case Success(id) => true
28 | case Failure(str) => error(this,s"addAfterFire failure: $str"); false
29 | }
30 | def removeAfterFire(name: String) = await[Answer](ref, RemoveAfterFireTrigger(name)) match {
31 | case Success(id) => true
32 | case Failure(str) => error(this,s"removeAfterFire failure: $str"); false
33 | }
34 | def addSilenceRequested(triggerId: String)(f: => Any) = await[Answer](ref, AddSilenceRequestedTrigger(triggerId, () => f)) match {
35 | case Success(id) => true
36 | case Failure(str) => error(this,s"addSilenceRequested failure: $str"); false
37 | }
38 | def removeSilenceRequested(name: String) = await[Answer](ref, RemoveSilenceRequestedTrigger(name)) match {
39 | case Success(id) => true
40 | case Failure(str) => error(this,s"removeSilenceRequested failure: $str"); false
41 | }
42 | def addSignalIgnored(triggerId: String)(f: => Any) = await[Answer](ref, AddSignalIgnoredTrigger(triggerId, () => f)) match {
43 | case Success(id) => true
44 | case Failure(str) => error(this,s"addSignalIgnored failure: $str"); false
45 | }
46 | def removeSignalIgnored(name: String) = await[Answer](ref, RemoveSignalIgnoredTrigger(name)) match {
47 | case Success(id) => true
48 | case Failure(str) => error(this,s"removeSignalIgnored failure: $str"); false
49 | }
50 |
51 | def removeAllTriggers() = await[Answer](ref, RemoveAllTriggers) match {
52 | case Success(id) => true
53 | case Failure(str) => error(this,s"removeAllTriggers failure: $str"); false
54 | }
55 |
56 | def reset() = await[Answer](ref, Reset) match {
57 | case Success(id) => true
58 | case Failure(str) => error(this,s"reset failure: $str"); false
59 | }
60 |
61 | def +=(signal: Double) = ref ! Signal(signal, id)
62 | def !(any: Any) = ref ! any
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/scala/anna/async/Messages.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.async.NeuronTriggers.Trigger
4 | import anna.data.{NeuronData, SynapseTrait}
5 |
6 | object Messages {
7 | // signals
8 | case class Signal(s: Double, senderId: String)
9 | case class SignalList(inputs: List[Double])
10 |
11 | // commands
12 | case object SilenceRequest // become silent
13 | case object WakeRequest // force wake up
14 | case class Connect(destinationRef: NeuronRef, weight: SynapseTrait)
15 | case class Disconnect(destinationId: String)
16 | case class CreateNeuron(data: NeuronData)
17 | case class SetInputs(ids: Seq[String])
18 | //case object NeuronShutdown
19 | case object Shutdown // net shutdown
20 | case object Reset
21 | case object RemoveAllTriggers
22 | case class SetSynapses(synapses: Seq[Synapse])
23 |
24 | // questions
25 | case object GetId
26 | case object GetInput
27 | case object GetLastOutput
28 | case class FindSynapse(destinationId: String)
29 | case object GetSynapses
30 | case class GetNeuron(id: String)
31 | case object GetNeurons
32 | case object GetInputs
33 | case object GetData
34 |
35 | // answers
36 | abstract class Answer
37 | case class Success(id: String) extends Answer // successful execution of the command
38 | case class Failure(error: String) extends Answer // error while executing the command
39 | case class NeuronShutdownDone(id: String) extends Answer // a special case - successful shutdown of a neuron
40 | case class NetShutdownDone(id: String) extends Answer // a special case - successful shutdown if the whole net
41 | case class Msg(d: Double, str: String) extends Answer // general answer to a question about a number or an id
42 | case class MsgSynapse(synapseOpt: Option[Synapse]) extends Answer // sends back a synapse
43 | case class MsgSynapses(synapses: Seq[Synapse]) extends Answer // sends back all synapses of the neuron
44 | case class MsgNeuron(neuronOpt: Option[NeuronRef]) extends Answer
45 | case class MsgNeurons(neurons: Seq[NeuronRef]) extends Answer
46 | case class NeuronInfo(id: String,
47 | netId: String,
48 | threshold: Double,
49 | silenceIterations: Int,
50 | synapses: List[SynapseInfo],
51 | buffer: Double)
52 | case class SynapseInfo(neuronId: String, weight: SynapseTrait)
53 |
54 |
55 | // triggers
56 | case class AddAfterFireTrigger(id: String, f: Trigger)
57 | case class RemoveAfterFireTrigger(id: String)
58 | case class AddSilenceRequestedTrigger(id: String, f: Trigger)
59 | case class RemoveSilenceRequestedTrigger(id: String)
60 | case class AddSignalIgnoredTrigger(id: String, f: Trigger)
61 | case class RemoveSignalIgnoredTrigger(id: String)
62 |
63 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/async/NeuronCounter.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.Context
4 |
5 | import scala.collection.mutable
6 | import anna.logger.LOG._
7 | import anna.logger.LOG
8 |
9 | import akka.actor.{PoisonPill, ActorRef, TypedProps, TypedActor}
10 |
11 | /**
12 | * Created by gorywoda on 23.06.15.
13 | */
14 |
15 |
16 | trait NeuronCounter {
17 | def reg(netId: String, neuronId: String, ref: ActorRef): Unit
18 | def unreg(netId: String, neuronId: String): Unit
19 | def neurons: Map[String, ActorRef]
20 | def size: Int
21 | def clean(): Unit
22 | }
23 |
24 | class NeuronCounterImpl(val name: String) extends NeuronCounter {
25 | def this() = this("default")
26 |
27 | LOG.addLogToFile("neuroncounter.log")
28 |
29 | private val _neurons = mutable.Map[String, ActorRef]()
30 |
31 | def reg(netId: String, neuronId: String, ref: ActorRef):Unit = {
32 | val _id = NeuronCounter.id(netId, neuronId)
33 | if (_neurons.contains(_id)) exception(s"The neuron ${_id} is already registered")
34 | _neurons += _id -> ref
35 | }
36 |
37 | def unreg(netId: String, neuronId: String):Unit = {
38 | _neurons -= NeuronCounter.id(netId, neuronId)
39 | }
40 |
41 | def neurons:Map[String, ActorRef] = _neurons.toMap
42 |
43 | def size:Int = _neurons.size
44 |
45 | def clean():Unit = if(_neurons.nonEmpty){
46 | val str = _neurons.map(_._1).mkString(",")
47 | error(this,s"${_neurons.size} needs cleaning, which means something went wrong... $str")
48 | _neurons.foreach(t => t._2 ! PoisonPill)
49 | _neurons.clear()
50 | }
51 | }
52 |
53 | object NeuronCounter {
54 | private var instanceOpt: Option[NeuronCounter] = None
55 |
56 | def set(nc: NeuronCounter) = {
57 | stop()
58 | instanceOpt = Some(nc)
59 | }
60 |
61 | def apply() = instanceOpt match {
62 | case Some(instance) => instance
63 | case None =>
64 | val instance = TypedActor(Context().system).typedActorOf(TypedProps(classOf[NeuronCounter], new NeuronCounterImpl("default")))
65 | instanceOpt = Some(instance)
66 | instance
67 | }
68 |
69 | def reg(netId: String, neuronId: String, ref: ActorRef) = if(_enabled) apply().reg(netId, neuronId, ref)
70 |
71 | def unreg(netId: String, neuronId: String) = if(_enabled) apply().unreg(netId, neuronId)
72 |
73 | def neurons = if(_enabled) apply().neurons else Map[String, ActorRef]()
74 |
75 | def size = if(_enabled) apply().size else -1
76 |
77 | def clean() = if(_enabled) apply().clean()
78 |
79 | def stop() = if(_enabled && instanceOpt != None){
80 | instanceOpt.get.clean()
81 | TypedActor(Context().system).poisonPill(instanceOpt.get)
82 | instanceOpt = None
83 | }
84 |
85 | private var _enabled = true
86 |
87 | def enabled = _enabled
88 | def disable = { _enabled = false }
89 | def enable = { _enabled = true }
90 |
91 | def id(netId: String, neuronId: String) = s"${netId}###${neuronId}"
92 | }
93 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Artificial Neural Networks in Akka
2 | ==================================
3 |
4 | ### Premise
5 |
6 | This project is an attempt to use an artificial neural network as a data flow transformer. Having an input stream of symbols which can be decoded into an input vector, the network will be able to generate a stream of more abstract symbols, using as additional information both the context (ie. data which was received before) and time gaps between consecutive chunks of data. The network is reactive - its computations are triggered by the input stream itself, not by a third agent watching the stream and sending requests to the network - and it can simultaneously receive and generate data.
7 |
8 | ### Documentation
9 | * [Abstract]
10 | * [Full article]
11 | * [A video from my gig on Scalar 2017](https://www.youtube.com/watch?v=5r4LSQT7Uc4&t=1s)
12 | * [Further ideas]
13 |
14 | If you want to look at only one class in this project then it's probably [this one](https://github.com/makingthematrix/ann/blob/SOSWithBlock_1.0/src/main/scala/anna/async/Neuron.scala).
15 |
16 | You can also check my other project: [GAI Library](https://github.com/makingthematrix/gailibrary), a small library for Artificial Intelligence in computer games, based on cellular automata. I focus on it now and I use it to learn Rust. When I develop it to a point when it's possible to use it in practical cases I plan to come back to ANN, and then switch between the two from time to time. (Hopefully I won't start writing yet another one).
17 |
18 | ### How to Install
19 | 1. You will need Java JDK 7+ and sbt 0.13.8 or newer.
20 | * [OpenJDK]– for Linux
21 | * [Java (Oracle)] – for any platform (Linux also)
22 | * [sbt - Linux]
23 | * [sbt - Windows]
24 | * The MSI installer is no longer supported, as far as I know, so you have to download and unpack the zip file.
25 | * [sbt - Mac]
26 | 2. You can check if sbt works simply typing **sbt** in the command line
27 | 3. Download the ANNA project, either through GIT, or by downloading the zip file. The current stable branch is `master`.
28 | 4. Go to the main project directory (“ann”) and type `sbt compile`. **sbt** will download and install Scala and Akka if you haven't done it before. Be patient. Then type `sbt console`. You will see the welcome screen. Type `help`.
29 |
30 | [Abstract]:
31 | [Full article]:
32 | [Further ideas]:
33 | [OpenJDK]:
34 | [Java (Oracle)]:
35 | [sbt - Linux]:
36 | [sbt - Windows]:
37 | [sbt - Mac]:
38 |
--------------------------------------------------------------------------------
/src/test/scala/anna/async/SilenceRequestSuite.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.Context
4 | import anna.async.NetBuilderOps._
5 | import anna.data.Silence
6 | import anna.logger.LOG
7 | import org.junit.Assert._
8 | import org.junit.Test
9 | import org.scalatest.junit.JUnitSuite
10 |
11 | import scala.concurrent.{Await, Promise}
12 |
13 | class SilenceRequestSuite extends JUnitSuite {
14 |
15 | val threshold = Context().threshold
16 | val silenceIterations = Context().silenceIterations
17 | val timeout = Context().timeout
18 |
19 | @Test def shouldSendSilenceRequest(){
20 | val net = NetRef("net1")
21 |
22 | val n1 = net.createNeuron("id1", threshold, silenceIterations)
23 | val n2 = net.createNeuron("id2", threshold, silenceIterations)
24 |
25 | n1.setSynapses(List(Synapse(n2,Silence())))
26 | net.setInputs(List(n1.id))
27 |
28 | val p = Promise[Boolean]()
29 |
30 | n2.addSilenceRequested("id2_silence"){
31 | LOG.debug("received silence request in id2")
32 | p.success(true)
33 | }
34 |
35 | net.signal(List(1.0))
36 |
37 | val silenceRequestReceived = Await.result(p.future, timeout.duration)
38 | assertTrue(silenceRequestReceived)
39 |
40 | net.shutdown()
41 | }
42 |
43 | @Test def shouldSendSilenceRequestThroughBuilder(){
44 | val netWrapper = NetBuilder().addInput("id1").addMiddle("id2")
45 | .use("id1").silence("id2").build()
46 |
47 | var silenceRequestReceived = false
48 |
49 | netWrapper.addSilenceRequested("id2"){
50 | LOG.debug("received silence request in id2")
51 | silenceRequestReceived = true
52 | }
53 |
54 | netWrapper += "1"
55 |
56 | netWrapper.iterateUntilCalm()
57 | assertTrue(silenceRequestReceived)
58 |
59 | netWrapper.shutdown()
60 | }
61 |
62 | @Test def shouldUseSilencingNeuron(){
63 | val net = NetRef("net1")
64 |
65 | val n1 = net.createNeuron("id1", threshold, silenceIterations)
66 | val silencingNeuron = net.createSilencingNeuron("silencingneuron")
67 | val n2 = net.createNeuron("id2", threshold, silenceIterations)
68 |
69 | n1.setSynapses(List(Synapse(silencingNeuron)))
70 | silencingNeuron.setSynapses(List(Synapse(n2)))
71 | net.setInputs(List(n1.id))
72 |
73 | val p = Promise[Boolean]()
74 |
75 | n2.addSilenceRequested("id2_silence"){
76 | LOG.debug("received silence request in id2")
77 | p.success(true)
78 | }
79 |
80 | net.signal(List(1.0))
81 |
82 | val silenceRequestReceived = Await.result(p.future, timeout.duration)
83 | assertTrue(silenceRequestReceived)
84 |
85 | net.shutdown()
86 | }
87 |
88 | @Test def shouldUseSilencingNeuronWithBuilder(){
89 | val netWrapper = NetBuilder().addInput("id1").chainSilencingNeuron("silencingneuron").chain("id2").build()
90 |
91 | var silenceRequestReceived = false
92 |
93 | netWrapper.addSilenceRequested("id2"){
94 | LOG.debug("received silence request in id2")
95 | silenceRequestReceived = true
96 | }
97 |
98 | netWrapper += "1"
99 |
100 | netWrapper.iterateUntilCalm()
101 | assertTrue(silenceRequestReceived)
102 |
103 | netWrapper.shutdown()
104 | }
105 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/async/NetBuilderOps.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.blocks.{DelayGate, Sequencer, SignalSum}
4 | import anna.data._
5 |
6 | class NetBuilderOps(val builder: NetBuilder) extends AnyVal {
7 | private def chainMiddle(id: String,
8 | weight: SynapseTrait = builder.defWeight,
9 | threshold: Double = builder.defThreshold,
10 | silenceIterations: Int = builder.defSilenceIterations):NetBuilder =
11 | builder.chain(id, weight, threshold, silenceIterations)
12 |
13 | def chain(id: String,
14 | weight: Double,
15 | threshold: Double,
16 | silenceIterations: Int):NetBuilder =
17 | chainMiddle(id, SynapseWeight(weight), threshold, silenceIterations)
18 |
19 | def chain(id: String,
20 | weight: Double,
21 | threshold: Double):NetBuilder =
22 | chainMiddle(id, SynapseWeight(weight), threshold, builder.defSilenceIterations)
23 | def chain(id: String):NetBuilder = chainMiddle(id)
24 | def chain(id: String, weight: Double):NetBuilder = chainMiddle(id, SynapseWeight(weight))
25 |
26 | def chainHush(id: String, threshold: Double):NetBuilder = chainMiddle(id, Silence(), threshold)
27 |
28 | def loop(id: String,
29 | w1: SynapseTrait =builder.defWeight,
30 | threshold: Double =builder.defThreshold,
31 | w2: SynapseTrait =builder.defWeight):NetBuilder = {
32 | val n1 = builder.current
33 | if(builder.inputSet.contains(n1.id)) throw new IllegalArgumentException("You can loop only in the middle layer")
34 |
35 | chainMiddle(id, w1, threshold, builder.defSilenceIterations)
36 |
37 | builder.connect(n1.id, w2).use(n1.id)
38 | }
39 |
40 | def loop(id: String, w1: Double, threshold: Double, w2: Double):NetBuilder =
41 | loop(id, SynapseWeight(w1), threshold, SynapseWeight(w2))
42 | def loop(w1: Double, threshold: Double, w2: Double):NetBuilder = loop(builder.generateId(), w1, threshold, w2)
43 | def loop(w1: Double, w2: Double):NetBuilder = loop(builder.generateId(), w1, builder.defThreshold, w2)
44 | def loop():NetBuilder = loop(builder.generateId())
45 |
46 | def oscillator(name: String) = loop(name, 1.0, 0.5, -1.0)
47 | def oscillator() = loop(1.0, 0.5, -1.0)
48 |
49 | def chainOscillator(id: String, weight: Double, threshold: Double) =
50 | chainMiddle(id, SynapseWeight(weight), threshold).oscillator(id+"_osc")
51 | def chainOscillator(id: String, weight: Double) = chainMiddle(id, SynapseWeight(weight)).oscillator(id+"_osc")
52 | def chainOscillator(weight: Double) = chainMiddle(builder.generateId(), SynapseWeight(weight)).oscillator()
53 |
54 | def self(weight: SynapseTrait =builder.defWeight):NetBuilder = builder.connect(builder.current.id, weight)
55 |
56 | def connect(id: String, weight: Double) = builder.connect(id, SynapseWeight(weight))
57 |
58 | implicit private def fromNetBuilder(builder: NetBuilder):NetBuilderOps = NetBuilderOps.fromNetBuilder(builder)
59 |
60 | def delayGate(name: String, delay: Int, inputWeight: Double = 1.0, inputTreshold: Double = 0.0) =
61 | DelayGate(name, delay).chain(builder, inputWeight, inputTreshold)
62 |
63 | def signalSum(name: String, requiredSignals: Int) = SignalSum(name, requiredSignals).chain(builder)
64 |
65 | def sequencer(name: String, in1Id: String, in2Id: String) = Sequencer(name).chain(builder, in1Id, in2Id)
66 | }
67 |
68 | object NetBuilderOps {
69 | implicit def fromNetBuilder(builder: NetBuilder):NetBuilderOps = new NetBuilderOps(builder)
70 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/async/NetRef.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import akka.actor.{ActorRef, Props}
4 | import anna.Context
5 | import anna.async.Messages._
6 | import anna.data.NeuronData
7 | import anna.logger.LOG._
8 | import anna.utils.Utils.await
9 |
10 | class NetRef(val id: String, val ref: ActorRef) {
11 | implicit val timeout = Context().timeout
12 |
13 | private var _iteration = 0L
14 |
15 | def iteration = _iteration
16 |
17 | def !(any: Any) = ref ! any
18 |
19 | def inputIds = inputs.map( _.id )
20 | def inputSize = inputs.size
21 | def inputs = await[MsgNeurons](ref,GetInputs).neurons
22 |
23 | def neurons = await[MsgNeurons](ref,GetNeurons).neurons
24 | def neuronsSize = neurons.size
25 | def neuronsIds = neurons.map( _.id )
26 |
27 | def find(id: String) = await[MsgNeuron](ref, GetNeuron(id))
28 |
29 | def createNeuron(id: String, threshold: Double, silenceIterations: Int) =
30 | await[NeuronRef](ref, CreateNeuron(NeuronData(id, threshold, silenceIterations, Nil)))
31 |
32 | def createDummy(id: String, silenceIterations: Int) =
33 | await[NeuronRef](ref, CreateNeuron(NeuronData(id, silenceIterations)))
34 |
35 | def createSilencingNeuron(id: String) = await[NeuronRef](ref, CreateNeuron(NeuronData(id)))
36 |
37 | def setInputs(seq: List[String]) = await[Answer](ref, SetInputs(seq))
38 |
39 | def signal(seq: List[Double]) = {
40 | ref ! SignalList(seq)
41 | _iteration += 1
42 | }
43 |
44 | def shutdown() = await[NetShutdownDone](ref, Shutdown)
45 |
46 | def info(id: String):NeuronInfo = find(id).neuronOpt match {
47 | case Some(neuronRef) => neuronRef.info
48 | case None => throw new IllegalArgumentException(s"Unable to find neuron with id $id")
49 | }
50 |
51 | def addAfterFireToAll(name: String) (f: => Any) = neurons.foreach(_.addAfterFire(name)(f))
52 | def addAfterFire(id: String, name: String)(f: => Any):Unit = find(id).neuronOpt match {
53 | case Some(neuronRef) => neuronRef.addAfterFire(name)(f)
54 | case None => error(this,s"Unable to find neuron with id $id")
55 | }
56 | def addAfterFire(id: String)(f: => Any):Unit = addAfterFire(id, id)(f)
57 |
58 | def addSilenceRequested(id: String, name: String)(f: => Any):Unit = find(id).neuronOpt match {
59 | case Some(neuronRef) => neuronRef.addSilenceRequested(name)(f)
60 | case None => error(this,s"Unable to find neuron with id $id")
61 | }
62 | def addSilenceRequested(id: String)(f: => Any):Unit = addSilenceRequested(id, id)(f)
63 |
64 | def addSignalIgnored(id: String, name: String)(f: => Any):Unit = find(id).neuronOpt match {
65 | case Some(neuronRef) => neuronRef.addSignalIgnored(name)(f)
66 | case None => error(this,s"Unable to find neuron with id $id")
67 | }
68 | def addSignalIgnored(id: String)(f: => Any):Unit = addSignalIgnored(id, id)(f)
69 |
70 | def reset() = await[Success](ref,Reset)
71 | def removeAllTriggers() = await[Success](ref, RemoveAllTriggers)
72 |
73 | def removeAfterFire(id:String) = await[Success](ref, RemoveAfterFireTrigger(id))
74 | def removeAfterFireFromAll(name: String) = neurons.foreach(_.removeAfterFire(name))
75 | def removeSilenceRequested(id:String) = await[Success](ref, RemoveSilenceRequestedTrigger(id))
76 | def removeSilenceRequestedFromAll(name: String) = neurons.foreach(_.removeSilenceRequested(name))
77 | def removeSignalIgnored(id:String) = await[Success](ref, RemoveSignalIgnoredTrigger(id))
78 | def removeSignalIgnoredFromAll(name: String) = neurons.foreach(_.removeSignalIgnored(name))
79 | }
80 |
81 | object NetRef {
82 | private var netRefOpt: Option[NetRef] = None
83 |
84 | def apply(id: String):NetRef = {
85 | val ref = Context().system.actorOf(Props(new Net(id)))
86 | val netRef = new NetRef(id, ref)
87 | netRefOpt = Some(netRef)
88 | netRef
89 | }
90 |
91 | def get = netRefOpt
92 | }
--------------------------------------------------------------------------------
/src/test/scala/anna/blocks/SequencerSuite.scala:
--------------------------------------------------------------------------------
1 | package anna.blocks
2 |
3 | import anna.async.{NetBuilder, NetWrapper}
4 | import anna.async.NetBuilderOps._
5 | import anna.logger.LOG
6 | import org.junit.Assert._
7 | import org.junit.{After, Test}
8 | import org.scalatest.junit.JUnitSuite
9 |
10 | /**
11 | * Created by samuel on 17.06.17.
12 | */
13 | class SequencerSuite extends JUnitSuite {
14 | private var _wrapper: Option[NetWrapper] = None
15 |
16 | @After def tearDown(): Unit = _wrapper match {
17 | case Some(wrapper) => wrapper.shutdown(); _wrapper = None
18 | case None =>
19 | }
20 |
21 | private def set(wrapper: NetWrapper) = _wrapper = Some(wrapper)
22 |
23 | private def netWrapper = _wrapper.get
24 |
25 | private def sequencer(blockName: String) =
26 | set(NetBuilder().addInput("in1").addInput("in2").sequencer(blockName, "in1", "in2").build())
27 |
28 | private def sequencerSilencer(blockName: String) = set(
29 | NetBuilder().addInput("in1").addInput("in2").sequencer(blockName, "in1", "in2")
30 | .addInput("in3").silence("sequencers")
31 | .build()
32 | )
33 |
34 | @Test def shouldAccept1001(): Unit = {
35 | sequencer("sequencer")
36 |
37 | var fired = false
38 | netWrapper.addAfterFire("sequencer2"){ fired = true }
39 | netWrapper.iterateUntilCalm("10,01")
40 |
41 | assertTrue(fired)
42 | }
43 |
44 | @Test def shouldIgnore10(): Unit = {
45 | sequencer("sequencer")
46 | var fired = false
47 |
48 | netWrapper.addAfterFire("sequencer2"){ fired = true }
49 | netWrapper.iterateUntilCalm("10")
50 |
51 | assertFalse(fired)
52 | }
53 |
54 | @Test def shouldIgnore01(): Unit = {
55 | sequencer("sequencer")
56 | var fired = false
57 |
58 | netWrapper.addAfterFire("sequencer2"){ fired = true }
59 | netWrapper.iterateUntilCalm("01")
60 |
61 | assertFalse(fired)
62 | }
63 |
64 | @Test def shouldIgnore1010(): Unit = {
65 | sequencer("sequencer")
66 | var fired = false
67 |
68 | netWrapper.addAfterFire("sequencer2"){ fired = true }
69 | netWrapper.iterateUntilCalm("10,10")
70 |
71 | assertFalse(fired)
72 | }
73 |
74 |
75 | @Test def shouldIgnore0101(): Unit = {
76 | sequencer("sequencer")
77 | var fired = false
78 |
79 | netWrapper.addAfterFire("sequencer2"){ fired = true }
80 | netWrapper.iterateUntilCalm("10,10")
81 |
82 | assertFalse(fired)
83 | }
84 |
85 |
86 | @Test def shouldAcceptTwoConsecutive(): Unit = {
87 | sequencer("sequencer")
88 | var fired = 0
89 |
90 | netWrapper.addAfterFire("sequencer2"){ fired += 1 }
91 | netWrapper.iterateUntilCalm("10,01,10,01")
92 |
93 | assertEquals(2, fired)
94 | }
95 |
96 | @Test def shouldAcceptOnlyFirst1(): Unit = {
97 | sequencer("sequencer")
98 | var fired = 0
99 |
100 | netWrapper.addAfterFire("sequencer2"){ fired += 1 }
101 | netWrapper.iterateUntilCalm("10,01,10,10")
102 |
103 | assertEquals(1, fired)
104 | }
105 |
106 | @Test def shouldAcceptOnlyFirst2(): Unit = {
107 | sequencer("sequencer")
108 | var fired = 0
109 |
110 | netWrapper.addAfterFire("sequencer2"){ fired += 1 }
111 | netWrapper.iterateUntilCalm("10,01,01,01")
112 |
113 | assertEquals(1, fired)
114 | }
115 |
116 | @Test def shouldAcceptOnlySecond1(): Unit = {
117 | sequencer("sequencer")
118 | var fired = 0
119 |
120 | netWrapper.addAfterFire("sequencer2"){ fired += 1 }
121 | netWrapper.iterateUntilCalm("10,10,10,01")
122 |
123 | assertEquals(1, fired)
124 | }
125 |
126 | @Test def shouldAcceptOnlySecond2(): Unit = {
127 | sequencer("sequencer")
128 | var fired = 0
129 |
130 | netWrapper.addAfterFire("sequencer2"){ fired += 1 }
131 | netWrapper.iterateUntilCalm("01,01,10,01")
132 |
133 | assertEquals(1, fired)
134 | }
135 |
136 | @Test def shouldReset(): Unit = {
137 | sequencerSilencer("sequencer")
138 | var fired = 0
139 |
140 | netWrapper.addAfterFire("sequencer2"){ fired += 1 }
141 | netWrapper.iterateUntilCalm("100,010")
142 | assertEquals(1, fired)
143 | netWrapper.iterateUntilCalm("100,001,010")
144 | assertEquals(1, fired)
145 | netWrapper.iterateUntilCalm("100,001,100,010")
146 | assertEquals(2, fired)
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/main/scala/anna/async/NetWrapper.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.Context
4 | import anna.logger.LOG
5 | import anna.utils.Utils._
6 |
7 | import scala.collection.mutable
8 |
9 | class NetWrapper(val net: NetRef) {
10 | lazy val ids = net.inputIds
11 | lazy val size = net.inputSize
12 |
13 | private val signRegister = mutable.Map[Char,Double]()
14 | private val inputQueue = mutable.Queue[List[Double]]()
15 | private var _iteration = 1
16 |
17 | def iteration = _iteration
18 |
19 | def resetIterations() = {
20 | _iteration = 1
21 | }
22 |
23 | def find(id: String) = net.find(id).neuronOpt match {
24 | case Some(nref) => nref
25 | case None => throw new IllegalArgumentException(s"There is no output neuron with id $id")
26 | }
27 |
28 | def info(id: String) = find(id).info
29 |
30 | def add(input: List[Double]) = {
31 | assert(input.length == size, s"The input vector has to be exactly ${size} numbers long and is ${input.length}.")
32 | inputQueue += input
33 | }
34 |
35 | def addEmptyInput = add(generateEmptyInput)
36 | def +=(input: List[Double]) = add(input)
37 | def +=(d: Double) = add(List(d))
38 | def +=(t: (Double,Double)) = add(List(t._1,t._2))
39 | def +=(t: (Double,Double,Double)) = add(List(t._1,t._2,t._3))
40 |
41 | def generateEmptyInput = List.fill(size)(0.0)
42 |
43 | def regSign(sign: Char,input: Double) = signRegister += (sign -> input)
44 |
45 | def +=(input: String) = {
46 | val in = input.split(",").toList
47 | in.map( str => str.toCharArray.map(
48 | c => if(signRegister.contains(c)) signRegister(c)
49 | else throw new IllegalArgumentException(s"No input registered with sign $c")
50 | ).toList).foreach( add )
51 | }
52 |
53 | def +=(c: Char) = {
54 | if(signRegister.contains(c)) add(List(signRegister(c)))
55 | else throw new IllegalArgumentException(s"No input registered with sign $c")
56 | }
57 |
58 | def deregSign(sign: Char) = signRegister -= sign
59 |
60 | def tick(c: Char):Unit = {
61 | this += c
62 | tick(1)
63 | }
64 |
65 | private def popInput() = if(inputQueue.nonEmpty) inputQueue.dequeue else generateEmptyInput
66 |
67 | def tick():Unit = tick(1)
68 |
69 | def tick(n: Int):Unit = {
70 | for(i <- 1 to n) {
71 | val input = popInput()
72 | LOG.debug(s"input: $input")
73 | if(input.sum > 0.0) { // so if the input is empty we do nothing
74 | net.signal(input)
75 | }
76 | Thread.sleep(Context().iterationTime)
77 | _iteration = _iteration + 1
78 | }
79 | }
80 |
81 | def iterateUntilCalm(inputVector: String):Int = {
82 | this += inputVector
83 | iterateUntilCalm()
84 | }
85 |
86 | def iterateUntilCalm():Int = {
87 | var neuronFired = false
88 | net.addAfterFireToAll("tickUntilCalm"){ neuronFired = true }
89 |
90 | var (calmTick, counter) = (0, 0)
91 | val maxIterations = Context().maxRunIterations
92 | while(inputQueue.nonEmpty || (calmTick < 3 && counter < maxIterations)){
93 | neuronFired = false
94 | tick()
95 | if(neuronFired) calmTick = 0 else calmTick += 1
96 | counter += 1
97 | }
98 |
99 | net.removeAfterFireFromAll("tickUntilCalm")
100 | counter
101 | }
102 |
103 | def addAfterFire(id: String, name: String)(f: => Any): Unit = net.addAfterFire(id, name)(f)
104 | def addAfterFire(id: String)(f: => Any): Unit = addAfterFire(id, id)(f)
105 |
106 | def addSilenceRequested(id: String, name: String)(f: => Any): Unit = net.addSilenceRequested(id, name)(f)
107 | def addSilenceRequested(id: String)(f: => Any):Unit = addSilenceRequested(id, id)(f)
108 |
109 | def addSignalIgnored(id: String, name: String)(f: => Any): Unit = net.addSignalIgnored(id, name)(f)
110 | def addSignalIgnored(id: String)(f: => Any):Unit = addSignalIgnored(id, id)(f)
111 |
112 | def shutdown() = {
113 | net.shutdown()
114 | Thread.sleep(200L)
115 | }
116 |
117 | def reset() = net.reset()
118 | def removeAllTriggers() = net.removeAllTriggers()
119 |
120 | def empty = inputQueue.isEmpty
121 |
122 | def neuronIds = net.neuronsIds
123 | }
124 |
125 | object NetWrapper {
126 | def apply(net: NetRef) = {
127 | val ani = new NetWrapper(net)
128 | ani.regSign('0',0.0)
129 | ani.regSign('1', 1.0)
130 | ani
131 | }
132 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/async/Net.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import akka.actor._
4 | import anna.async.Messages._
5 | import anna.data.NeuronData
6 |
7 | import scala.collection.mutable
8 |
9 | class Net(val id: String) extends Actor {
10 | private val neurons = mutable.ListBuffer[NeuronRef]()
11 | private var inputs = List[NeuronRef]()
12 |
13 | def receive: Receive = {
14 | case GetId => sender ! Msg(0.0, id)
15 | case GetNeurons => sender ! MsgNeurons(neurons.toList)
16 | case CreateNeuron(data) => createNeuron(data)
17 | case Shutdown => shutdown()
18 | case GetInputs => sender ! MsgNeurons(inputs)
19 | case SetInputs(ids) => setInputs(ids.toSet)
20 | case GetNeuron(id) => sender ! MsgNeuron(findRef(id))
21 | case SignalList(in) => signal(in)
22 | case Reset => resetBuffer()
23 | case RemoveAllTriggers => removeTriggers()
24 | case RemoveAfterFireTrigger(id) => removeAfterFire(id)
25 | case Terminated(actorRef) =>
26 | neurons -= neurons.find( _.ref == actorRef ).get
27 | if(neurons.isEmpty) self ! PoisonPill
28 | }
29 |
30 | private var shutdownCallerOpt:Option[ActorRef] = None
31 |
32 | override def preStart():Unit = {
33 |
34 | }
35 |
36 | override def postStop():Unit = {
37 | if(shutdownCallerOpt != None) shutdownCallerOpt.get ! NetShutdownDone(id)
38 | }
39 |
40 | private def shutdown() = {
41 | shutdownCallerOpt = Some(sender)
42 | if(neurons.nonEmpty) {
43 | neurons.foreach(_ ! PoisonPill)
44 | } else {
45 | self ! PoisonPill
46 | }
47 | }
48 |
49 | def waiting(caller: ActorRef, waitingFor: Set[String], title: String = ""): Receive = {
50 | case Success(id) =>
51 | val newWaitingFor = waitingFor - id
52 | if(newWaitingFor.isEmpty){
53 | caller ! Success(id)
54 | context.become(receive)
55 | } else context.become(waiting(caller, newWaitingFor, title))
56 | }
57 |
58 | private def resetBuffer() = {
59 | context.become( waiting(sender, neurons.map(_.id).toSet, "resetting") )
60 | neurons.foreach(_ ! Reset)
61 | }
62 |
63 | private def removeAfterFire(id:String) = {
64 | context.become( waiting(sender, neurons.map(_.id).toSet, s"removing an after fire trigger $id") )
65 | neurons.foreach(_.removeAfterFire(id))
66 | }
67 |
68 | private def removeTriggers() = {
69 | context.become( waiting(sender, neurons.map(_.id).toSet, "removing triggers") )
70 | neurons.foreach(_ ! RemoveAllTriggers)
71 | }
72 |
73 | private def add(id: String, ref: ActorRef) = {
74 | val neuronRef = new NeuronRef(id, ref)
75 | neurons += neuronRef
76 | context.watch(ref)
77 | sender ! neuronRef
78 | }
79 |
80 | private def createNeuron(data:NeuronData) = data.neuronType match {
81 | case NeuronTypeStandard() => createStandard(data.id, data.threshold, data.silenceIterations)
82 | case NeuronTypeDummy() => createDummy(data.id, data.silenceIterations)
83 | case NeuronTypeSilencing() => createSilencing(data.id)
84 | }
85 |
86 | private def createStandard(id: String, threshold: Double, silenceIterations: Int) = {
87 | val ref = context.actorOf(Props(new Neuron(id, this.id, threshold, silenceIterations)))
88 | add(id, ref)
89 | }
90 |
91 | private def createDummy(id: String, silenceIterations: Int) = {
92 | val ref = context.actorOf(Props(new DummyNeuron(id, this.id)))
93 | add(id, ref)
94 | }
95 |
96 | private def createSilencing(id: String) = {
97 | val ref = context.actorOf(Props(new SilencingNeuron(id, this.id)))
98 | add(id, ref)
99 | }
100 |
101 | private def signal(in: List[Double]){
102 | assert(in.size == inputs.size, s"Difference in size between the input layer (${inputs.size}) and the input (${in.size})")
103 | inputs.zip(in).foreach {
104 | case (inputNeuron: NeuronRef, signal: Double) => inputNeuron ! Signal(signal, "Input")
105 | }
106 | }
107 |
108 | private def setInputs(ids: Set[String]){
109 | inputs = neurons.filter( n => ids.contains(n.id) ).toList.sortBy(_.id)
110 | if(inputs.size != ids.size){
111 | val inIds = inputs.map( _.id )
112 | val notFound = ids.filterNot( inIds.contains(_) )
113 | sender ! Failure(s"setInputs, unable to find neurons with ids: $notFound")
114 | } else {
115 | sender ! Success("setInputLayer_" + id)
116 | }
117 | }
118 |
119 | private def findRef(id: String):Option[NeuronRef] = neurons.find(_.id == id)
120 | }
121 |
122 |
123 |
--------------------------------------------------------------------------------
/src/main/scala/anna/Context.scala:
--------------------------------------------------------------------------------
1 | package anna
2 |
3 | import java.util.concurrent.TimeUnit
4 |
5 | import akka.actor.ActorSystem
6 | import akka.util.Timeout
7 | import anna.data._
8 | import anna.utils.Utils.formats
9 | import com.typesafe.config.ConfigFactory
10 | import org.json4s.native.Serialization.{read, writePretty}
11 |
12 | import anna.logger.LOG._
13 |
14 | import scala.concurrent.duration._
15 |
16 | case class NeuronDefaults(threshold: Double, weight: SynapseTrait, silenceIterations: Int, iterationTime: Long){
17 | def toJson = writePretty(this)
18 | }
19 |
20 | case class Context(awaitTimeout: Long, maxRunIterations: Long, neuronDefaults: NeuronDefaults){
21 | def timeout = Timeout(FiniteDuration.apply(awaitTimeout, TimeUnit.SECONDS))
22 | def threshold = neuronDefaults.threshold
23 | def weight = neuronDefaults.weight
24 | def silenceIterations = neuronDefaults.silenceIterations
25 | def iterationTime = neuronDefaults.iterationTime
26 |
27 | private var systemOpt: Option[ActorSystem] = None
28 |
29 | def system:ActorSystem = systemOpt match {
30 | case Some(actorSystem) => actorSystem
31 | case None =>
32 | implicit val t = timeout
33 | val actorSystem = ActorSystem("system")
34 | systemOpt = Some(actorSystem)
35 | actorSystem
36 | }
37 |
38 | def shutdownSystem() = if(systemOpt != None) {
39 | systemOpt.get.shutdown()
40 | systemOpt = None
41 | }
42 |
43 | def toJson = writePretty(this)
44 |
45 | }
46 |
47 | object Context {
48 | private var instance:Option[Context] = None
49 |
50 | def apply(): Context = {
51 | if(instance == None) init()
52 | instance.get
53 | }
54 |
55 | def set(newInstance: Context): Unit ={
56 | instance = Some(newInstance)
57 | }
58 |
59 | def reset(): Unit ={
60 | instance = None
61 | }
62 |
63 | private final def that = instance.get
64 |
65 | def withAwaitTimeout(timeout: Long) =
66 | set(apply().copy(awaitTimeout = timeout))
67 | def withMaxRunIterations(iterations: Long) =
68 | set(apply().copy(maxRunIterations = iterations))
69 | def withThreshold(threshold: Double) =
70 | set(apply().copy(neuronDefaults = that.neuronDefaults.copy(threshold = threshold)))
71 | def withWeight(weight: SynapseTrait) =
72 | set(apply().copy(neuronDefaults = that.neuronDefaults.copy(weight = weight)))
73 | def withSilenceIterations(silenceIterations: Int) =
74 | set(apply().copy(neuronDefaults = that.neuronDefaults.copy(silenceIterations = silenceIterations)))
75 | def withIterationTime(iterationTime: Long) =
76 | set(apply().copy(neuronDefaults = that.neuronDefaults.copy(iterationTime = iterationTime)))
77 |
78 | val _awaittimeout = "awaitTimeout"
79 | val _maxruniterations = "maxRunIterations"
80 | val _neurondefaults = "neuronDefaults"
81 | val _defaultthreshold = "defaultThreshold"
82 | val _defaultweight = "defaultWeight"
83 | val _defaultsilenceiterations = "defaultSilenceIterations"
84 | val _defaultiterationtime = "defaultIterationTime"
85 |
86 | private def init(): Unit ={
87 | val config = ConfigFactory.load()
88 | val root = config.getConfig("context")
89 |
90 | val awaitTimeout = root.getInt(_awaittimeout)
91 | val maxRunIterations = root.getInt(_maxruniterations)
92 |
93 | // neuron defaults
94 | val neuronRoot = root.getConfig(_neurondefaults)
95 | val threshold = neuronRoot.getDouble(_defaultthreshold)
96 | val weight = SynapseTrait(neuronRoot.getString(_defaultweight))
97 | val silenceIterations = neuronRoot.getInt(_defaultsilenceiterations)
98 | val iterationTime = neuronRoot.getLong(_defaultiterationtime)
99 |
100 | val neuronDefaults = NeuronDefaults(threshold, weight, silenceIterations, iterationTime)
101 |
102 | set(Context(awaitTimeout, maxRunIterations, neuronDefaults))
103 | }
104 |
105 | def fromJson(jsonStr: String) = read[Context](jsonStr)
106 | def withJson(jsonStr: String) = set(fromJson(jsonStr))
107 |
108 | def set(name: String, n: Int):Unit = name match {
109 | case `_awaittimeout` => withAwaitTimeout(n)
110 | case `_maxruniterations` => withMaxRunIterations(n)
111 | case `_defaultsilenceiterations` => withSilenceIterations(n)
112 | }
113 |
114 | def set(name: String, d: Double):Unit = name match {
115 | case `_defaultthreshold` => withThreshold(d)
116 | case `_defaultweight` => withWeight(SynapseWeight(d))
117 | }
118 |
119 | def set(map: Map[String,Any]):Unit = map.foreach(tuple =>
120 | if(tuple._2.isInstanceOf[Double]) set(tuple._1, tuple._2.asInstanceOf[Double])
121 | else if(tuple._2.isInstanceOf[Int]) set(tuple._1, tuple._2.asInstanceOf[Int])
122 | else exception(this,s"Unsuppored type of ${tuple._1}: ${tuple._2.getClass}")
123 | )
124 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/data/NetData.scala:
--------------------------------------------------------------------------------
1 | package anna.data
2 |
3 | import anna.Context
4 | import anna.utils.Utils
5 | import anna.utils.Utils.{formats, synapseId}
6 | import org.json4s.native.Serialization.{read, writePretty}
7 |
8 | /**
9 | * Created by gorywoda on 03.01.15.
10 | */
11 |
12 | case class NetData(id: String,
13 | neurons: List[NeuronData],
14 | inputs: List[String], // time wasted refactoring this to Set[String]: 3h; please update when needed
15 | threshold: Double,
16 | silenceIterations: Int,
17 | weight: SynapseTrait){
18 | def withId(id: String) = copy(id = id)
19 | def withNeurons(neurons: List[NeuronData]) = copy(neurons = neurons)
20 | def withInputs(inputs: List[String]) = copy(inputs = inputs)
21 | def withThreshold(threshold: Double) = copy(threshold = threshold)
22 | def withSilenceIterations(silenceIterations: Int) = copy(silenceIterations = silenceIterations)
23 | def withWeight(weight: SynapseWeight) = copy(weight = weight)
24 | def neuron(neuronId: String) = neurons.find(n => n.id == neuronId || NetData.removeNetId(n.id) == neuronId)
25 | .getOrElse(throw new IllegalArgumentException(s"No neuron found with id $neuronId"))
26 | def contains(neuronId: String) = neurons.exists(n => n.id == neuronId || NetData.removeNetId(n.id) == neuronId)
27 | def synapses:Map[String,SynapseData] = neurons.flatMap(n => n.synapses.map(s => (synapseId(n.id, s.neuronId) -> s))).toMap
28 | def synapse(from: String, to: String) = neurons.find(n => n.id == from || NetData.removeNetId(n.id) == from) match {
29 | case Some(n) => n.synapses.find(s => s.neuronId == to || NetData.removeNetId(s.neuronId) == to)
30 | case None => None
31 | }
32 |
33 | def findIdsConnectedTo(neuronId: String) = neurons.filter(_.synapses.map(_.neuronId).contains(neuronId)).map(_.id)
34 |
35 | def toJson = writePretty(this)
36 |
37 | def filter(ids: Seq[String]) = {
38 | val idsSet = ids.toSet
39 | neurons.filter( n => idsSet.contains(n.id) )
40 | }
41 |
42 | def filterNot(ids: Seq[String]) = {
43 | val idsSet = ids.toSet
44 | neurons.filterNot( n => idsSet.contains(n.id) )
45 | }
46 |
47 | def validate(): Unit = {
48 | // so, what possibly can go wrong with a net?
49 | // trivial checks:
50 | // 1. The net id should not contain '_' as this is how we separate net id from neuron id
51 | assert(!id.contains("_"), "The net id should not contain '_' as this is how we separate net id from neuron id: " + id)
52 | // 2. There has to be at least one input neuron
53 | assert(inputs.nonEmpty, "There has to be at least one input neuron in the net: " + id)
54 | // 3. And it should be on the neurons list
55 | inputs.foreach( inId => assert(neurons.find(_.id == inId) != None, s"The input id $inId does not match any of the neurons in the net $id"))
56 | // 4. Threshold should be >=0 and <1
57 | assert(threshold >= 0.0 && threshold < 1.0,s"Threshold should be in <0.0,1.0) but is $threshold in the net $id")
58 | // 5. check if all synapses connect to existing neurons
59 | val nIdSet = neurons.map(_.id).toSet
60 | neurons.foreach(n => {
61 | n.synapses.foreach(s => assert(nIdSet.contains(s.neuronId), s"The synapse from ${n.id} to ${s.neuronId} leads to a neuron which does not exist in the net $id"))
62 | })
63 |
64 | // 6. one neuron should not have two synapses to the same neuron
65 | neurons.foreach( n => {
66 | val neuronIds = n.synapses.map(_.neuronId)
67 | val neuronIdsSet = neuronIds.toSet
68 | val doubledIds = neuronIds.filterNot( id => neuronIdsSet.contains(id) )
69 | assert(doubledIds.isEmpty, s"In the neuron ${n.id}, more than one synapse leads to the same neuron: $doubledIds")
70 | })
71 |
72 | // @todo: 7. check if there is no neuron with no synapse leading to it
73 | // disabled until I figure out how to (and if at all) filter out non-accessible output neurons
74 | // - they can happen, they cannot be deleted, but they don't necessarily are invalid
75 | //val idLeft = neurons.foldLeft(nIdSet)( (idSet: Set[String], n: NeuronData) => idSet -- n.synapses.map(_.neuronId)) -- inputs
76 | //assert(idLeft.isEmpty, s"There are neurons with no synapses leading to them: $idLeft in the net $id")
77 | }
78 | }
79 |
80 | object NetData {
81 | def apply(id: String):NetData = NetData(id, Nil, Nil)
82 | def apply(id: String, neurons: List[NeuronData], inputs: List[String]):NetData =
83 | NetData(id, neurons, inputs, Context().threshold,
84 | Context().silenceIterations, Context().weight)
85 |
86 | def fromJson(jsonStr: String) = read[NetData](jsonStr)
87 |
88 | def neuronId(netId: String, id: String): String = s"${netId}_$id"
89 | def neuronId(netId: String, index: Int): String = s"${netId}_$index"
90 | def removeNetId(id: String): String = if(id.contains("_")) id.substring(id.indexOf("_")+1) else id
91 |
92 | def replaceNetId(oldNeuronId: String, newNetId: String): String =
93 | if(oldNeuronId.contains("_")) newNetId + "_" + oldNeuronId.substring(oldNeuronId.indexOf("_")+1)
94 | else newNetId + "_" + oldNeuronId
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/scala/anna/utils/Utils.scala:
--------------------------------------------------------------------------------
1 | package anna.utils
2 |
3 | import java.io.{BufferedWriter, File, FileWriter, IOException}
4 | import java.text.{NumberFormat, ParsePosition}
5 | import java.util.{Calendar, Locale}
6 |
7 | import akka.actor.ActorRef
8 | import akka.pattern.ask
9 | import anna.Context
10 | import anna.async._
11 | import anna.data._
12 | import anna.logger.LOG
13 | import anna.logger.LOG._
14 | import org.apache.commons.io.FileUtils
15 | import org.json4s._
16 | import org.json4s.native.Serialization
17 |
18 | import scala.annotation.tailrec
19 | import scala.concurrent.Await
20 | import scala.util.Random
21 |
22 | object Utils {
23 | def fail(str: String):IllegalArgumentException = throw new IllegalArgumentException(str)
24 | def assert(condition: => Boolean, str: String) = if(!condition) fail(str)
25 |
26 | def round(x: Double, digits: Int = 2) = {
27 | val foo = math.pow(10, digits)
28 | math.round(x * foo) / foo
29 | }
30 |
31 | def minmax(min: Double, v: Double, max: Double) = Math.max(min,Math.min(max,v))
32 | def minmax(min: Int, v: Int, max: Int) = Math.max(min,Math.min(max,v))
33 |
34 | def minMaxClosed(x: Double, min: Double, max: Double, d: => Double) = x match {
35 | case _ if x <= min => min
36 | case _ if x >= max => max
37 | case _ => d
38 | }
39 |
40 | def minMaxOpen(x: Double, min: Double, max: Double, d: => Double) = x match {
41 | case _ if x < min => min
42 | case _ if x > max => max
43 | case _ => d
44 | }
45 |
46 | implicit val timeout = Context().timeout
47 |
48 | def await[T](ref: ActorRef, msg: Any): T = Await.result(ref ? msg, timeout.duration).asInstanceOf[T]
49 | def await[T](net: NetRef, msg: Any): T = await[T](net.ref, msg)
50 | def await[T](neuron: NeuronRef, msg: Any): T = await[T](neuron.ref, msg)
51 |
52 | def parseDouble(s: String) = {
53 | val pp = new ParsePosition(0)
54 | val d = NumberFormat.getInstance(Locale.ENGLISH).parse(s, pp)
55 | if (pp.getErrorIndex == -1) Some(d.doubleValue) else None
56 | }
57 |
58 | @tailrec
59 | def splitIdsRandomly(oldSet: Set[String], idsToDraw: Int, newSet: Set[String] = Set()):(Set[String],Set[String]) = idsToDraw match {
60 | case 0 => (newSet, oldSet)
61 | case n if oldSet.isEmpty => (Set(), newSet)
62 | case n =>
63 | val id = RandomNumber.apply(oldSet)
64 | splitIdsRandomly(oldSet.toSet - id, idsToDraw - 1, newSet + id)
65 | }
66 |
67 | def vary(d: Double) = d + (Random.nextDouble() * 0.1 - 0.05)
68 | def V = vary(0.95)
69 | def v = vary(0.05)
70 |
71 | def save(filePath: String, body: String) = {
72 | var writer:BufferedWriter = null // I blame Java
73 | try {
74 | val file = new File(filePath)
75 | if(file.exists()) file.delete()
76 |
77 | file.createNewFile()
78 | file.setReadable(true)
79 | file.setWritable(true)
80 |
81 | writer = new BufferedWriter(new FileWriter(file, true))
82 | writer.write(body)
83 | writer.newLine()
84 | writer.flush()
85 | } catch {
86 | case ex: IOException => LOG.exception(ex)
87 | } finally {
88 | if(writer != null) writer.close()
89 | }
90 |
91 | }
92 |
93 | def load(filePath: String) = {
94 | val source = scala.io.Source.fromFile(filePath)
95 | val lines = source.getLines().mkString("\n")
96 | source.close()
97 | lines
98 | }
99 |
100 | def createDir(filePath: String, deleteIfExists: Boolean =false) = {
101 | val dir = new File(filePath)
102 |
103 | if(dir.exists() && deleteIfExists) deleteDir(filePath)
104 |
105 | if(!dir.exists()){
106 | if(!dir.mkdir()) throw new IOException(s"Failed to create a directory: $filePath")
107 | }
108 |
109 | dir
110 | }
111 |
112 | def listDirs(dirPath: String) = list(dirPath, 1)
113 | def listFiles(dirPath: String) = list(dirPath, 2)
114 |
115 | private def list(dirPath: String, flag: Int) = {
116 | val dir = new File(dirPath)
117 | if(!dir.exists() || !dir.isDirectory) exception(this, s"No directory with the name $dir")
118 |
119 | (flag match {
120 | case 0 => dir.listFiles()
121 | case 1 => dir.listFiles().filter(_.isDirectory)
122 | case 2 => dir.listFiles().filterNot(_.isDirectory)
123 | }).map(_.getName).toList
124 | }
125 |
126 | def fileExists(filePath: String) = new File(filePath).exists()
127 |
128 | def deleteDir(filePath: String) = {
129 | val dir = new File(filePath)
130 | FileUtils.deleteDirectory(dir)
131 | !dir.exists()
132 | }
133 |
134 | implicit val formats = Serialization.formats(
135 | ShortTypeHints(
136 | List(
137 | classOf[SynapseWeight],
138 | classOf[Silence],
139 | classOf[NeuronTypeStandard],
140 | classOf[NeuronTypeDummy],
141 | classOf[NeuronTypeSilencing]
142 | )
143 | )
144 | )
145 |
146 | private def twociphers(n: Int) = if(n < 10) "0"+n.toString else n.toString
147 |
148 | def dateTag = {
149 | val cal = Calendar.getInstance
150 | StringBuilder.newBuilder
151 | .append(cal.get(Calendar.YEAR)).append('-')
152 | .append(twociphers(cal.get(Calendar.MONTH)+1)).append('-')
153 | .append(twociphers(cal.get(Calendar.DAY_OF_MONTH))).append('_')
154 | .append(twociphers(cal.get(Calendar.HOUR_OF_DAY))).append(':')
155 | .append(twociphers(cal.get(Calendar.MINUTE))).append(':')
156 | .append(twociphers(cal.get(Calendar.SECOND))).append('.')
157 | .append(cal.get(Calendar.MILLISECOND)).toString
158 | }
159 |
160 | def synapseId(fromId: String, toId: String) = s"$fromId->$toId"
161 | def fromId(id: String) = s"$id->"
162 | def toId(id: String) = s"->$id"
163 |
164 | }
165 |
166 |
--------------------------------------------------------------------------------
/src/main/scala/anna/async/Neuron.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import akka.actor._
4 | import anna.Context
5 | import anna.async.Messages._
6 | import anna.async.Neuron.SilenceIterations
7 | import anna.logger.LOG
8 | import anna.utils.Utils
9 |
10 | import scala.concurrent.ExecutionContext.Implicits.global
11 | import scala.concurrent.duration._
12 |
13 | class Neuron(val id: String,
14 | val netId: String,
15 | val threshold: Double,
16 | val silenceIterations: SilenceIterations,
17 | protected var synapses: List[Synapse] = List[Synapse]()
18 | ) extends Actor with NeuronTriggers {
19 | require(silenceIterations >= Neuron.SilenceForever)
20 | require(threshold >= 0.0 && threshold <= 1.0)
21 |
22 | implicit val that = this
23 |
24 | protected var buffer = 0.0
25 | private val schedulerBuffer = new SchedulerBuffer(context)
26 | override def preStart():Unit = {
27 | NeuronCounter.reg(netId, id, self)
28 | }
29 |
30 | override def postStop():Unit = {
31 | schedulerBuffer.clear()
32 | NeuronCounter.unreg(netId, id)
33 | }
34 |
35 | private def printBuffer = Utils.round(buffer)
36 |
37 | private var sleepingTimestamp: Option[Long] = None
38 | private def isSleeping = sleepingTimestamp != None
39 |
40 | private def makeSleep() = {
41 | LOG += s"going to sleep for ${Context().iterationTime} ms"
42 | sleepingTimestamp = Some(schedulerBuffer.schedule(Context().iterationTime millis){ wakeUp() })
43 | }
44 |
45 | private def wakeUp(){
46 | LOG += "waking up"
47 | sleepingTimestamp = None
48 | tick()
49 | }
50 |
51 | private def becomeSilent(){
52 | LOG += s"becomeSilent, silenceIterations is ${silenceIterations}"
53 | buffer = 0.0
54 | LOG += s"(becomeSilent) buffer is now $printBuffer"
55 |
56 | // if the neuron was sleeping, it's cancelled
57 | if(isSleeping){
58 | schedulerBuffer.unschedule(sleepingTimestamp.get)
59 | sleepingTimestamp = None
60 | }
61 |
62 | if(!silentForever){
63 | val t = Context().iterationTime * silenceIterations
64 | if(t > 0){
65 | context.become(silence)
66 | schedulerBuffer.schedule(t millis){ wakeFromSilence() }
67 | }
68 | } else {
69 | LOG += "becoming silent forever"
70 | context.become(silence)
71 | }
72 |
73 | triggerSilenceRequested()
74 | }
75 |
76 | private lazy val silentForever = silenceIterations == Neuron.SilenceForever
77 |
78 | private def wakeFromSilence() = {
79 | LOG += s"waking up from a silence request"
80 | context.become(receive)
81 | }
82 |
83 | protected def +=(signal: Double){
84 | LOG += s"adding signal $signal to buffer $printBuffer, threshold is $threshold"
85 | buffer += signal
86 | LOG += s"(after adding) buffer is now $printBuffer"
87 | if(!isSleeping) tick()
88 | }
89 |
90 | private def biggerOrCloseEnough(x: Double, y: Double) = { x > 0.0 && x + 0.001 > y }
91 |
92 | private def tick():Unit = this.synchronized {
93 | buffer = Utils.minmax(-1.0, buffer, 1.0)
94 |
95 | if (biggerOrCloseEnough(buffer, threshold)) {
96 | buffer = 0.0
97 |
98 | LOG += s"Fire!"
99 | makeSleep()
100 | synapses.foreach( _.send(1.0, id) )
101 |
102 | triggerAfterFire()
103 | }
104 |
105 | LOG += s"(tick) buffer is now $printBuffer"
106 | }
107 |
108 | protected def findSynapse(destinationId: String):Option[Synapse] =
109 | if(synapses.nonEmpty) synapses.find(_.dest.id == destinationId) else None
110 |
111 | protected def findSynapse(destination: Neuron):Option[Synapse] = findSynapse(destination.id)
112 |
113 | protected def answer(msg: Answer) = NetRef.get match {
114 | case Some(netref) => netref ! msg
115 | case None =>
116 | }
117 |
118 | private def reset(): Unit ={
119 | buffer = 0.0
120 | LOG += s"(reset) buffer is now $printBuffer"
121 |
122 | context.become(receive)
123 | schedulerBuffer.clear()
124 | answer(Success(id))
125 | }
126 |
127 | private def removeTriggers(): Unit ={
128 | removeAllTriggers()
129 | answer(Success(id))
130 | }
131 |
132 | def receive: Receive = activeBehaviour orElse commonBehaviour orElse otherBehaviour(s"$id, active")
133 |
134 | def silence: Receive = silentBehaviour orElse commonBehaviour orElse otherBehaviour(s"$id, silence")
135 |
136 | val activeBehaviour: Receive = {
137 | case Signal(s, senderId) =>
138 | LOG += s"signal $s from $senderId received"
139 | this += s
140 | case SilenceRequest =>
141 | LOG += s"silence request received"
142 | becomeSilent()
143 | }
144 |
145 | val silentBehaviour: Receive = {
146 | case Signal(s, senderId) =>
147 | LOG += s"signal $s from $senderId ignored"
148 | triggerSignalIgnored()
149 | case WakeRequest =>
150 | LOG += s"wake request received"
151 | wakeFromSilence()
152 | }
153 |
154 | val commonBehaviour: Receive = {
155 | case GetId => sender ! Msg(0.0, id)
156 | case GetData => sender ! info
157 | case FindSynapse(destinationId) => sender ! MsgSynapse(findSynapse(destinationId))
158 | case GetSynapses => sender ! MsgSynapses(synapses)
159 | case SetSynapses(synapses) => this.synapses = synapses.toList
160 | case AddAfterFireTrigger(triggerId, trigger) =>
161 | addAfterFire(triggerId, trigger)
162 | sender ! Success(triggerId)
163 | case RemoveAfterFireTrigger(triggerId) =>
164 | removeAfterFire(triggerId)
165 | sender ! Success(triggerId)
166 | case AddSilenceRequestedTrigger(triggerId, trigger) =>
167 | addSilenceRequested(triggerId, trigger)
168 | sender ! Success(triggerId)
169 | case RemoveSilenceRequestedTrigger(triggerId) =>
170 | removeSilenceRequested(triggerId)
171 | sender ! Success(triggerId)
172 | case AddSignalIgnoredTrigger(triggerId, trigger) =>
173 | addSignalIgnored(triggerId, trigger)
174 | sender ! Success(triggerId)
175 | case RemoveSignalIgnoredTrigger(triggerId) =>
176 | removeSignalIgnored(triggerId)
177 | sender ! Success(triggerId)
178 | case RemoveAllTriggers => removeTriggers()
179 | case Reset => reset()
180 | }
181 |
182 | def otherBehaviour(state: String): Receive = {
183 | case other => LOG += s"$state, unrecognized message: $other"
184 | }
185 |
186 | def info = NeuronInfo(id, netId, threshold, silenceIterations, synapses.map(_.info), buffer)
187 | }
188 |
189 | object Neuron {
190 | type SilenceIterations = Int
191 | val SilenceForever: SilenceIterations = -1
192 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/async/NetBuilder.scala:
--------------------------------------------------------------------------------
1 | package anna.async
2 |
3 | import anna.Context
4 | import anna.async.Messages._
5 | import anna.data.SynapseData.fromDouble
6 | import anna.data._
7 | import anna.logger.LOG._
8 | import anna.utils.Utils.{assert, await}
9 | import akka.pattern.ask
10 | import scala.collection.mutable
11 |
12 | import anna.logger.LOG
13 |
14 | class NetBuilder {
15 | var netId:String = "net"
16 |
17 | var defThreshold = Context().threshold
18 | var defSilenceIterations = Context().silenceIterations
19 | var defWeight:SynapseTrait = Context().weight
20 |
21 | private val neurons = mutable.Map[String, NeuronData]()
22 | private val synapses = mutable.Map[String, mutable.ListBuffer[SynapseData]]()
23 | private val ins = mutable.Set[String]()
24 | private val startSilent = mutable.Set[String]()
25 |
26 | private var currentNeuronId: Option[String] = None
27 |
28 | def inputSet = ins.toSet
29 |
30 | def generateId() = netId + "_" + nextIndex()
31 |
32 | private def addSynapse(fromId: String, toId: String, weight: SynapseTrait) =
33 | synapses.getOrElseUpdate(fromId, mutable.ListBuffer[SynapseData]()) += SynapseData(toId, weight)
34 |
35 | def contains(id: String) = neurons.contains(id)
36 |
37 | def use(id: String) = {
38 | assert(contains(id),s"There is no neuron with id $id")
39 | currentNeuronId = Some(id)
40 | this
41 | }
42 |
43 | def silence(id: String) = connect(id, Silence())
44 | def wake(id: String) = connect(id, Wake())
45 |
46 | def connect(id: String, weight: SynapseTrait) = {
47 | assert(contains(id),s"There is no neuron with id $id")
48 | addSynapse(current.id, id, weight)
49 | this
50 | }
51 |
52 | def current = currentNeuronId match {
53 | case Some(id) => neurons(id)
54 | case None => throw new IllegalArgumentException("There is no current neuron id set")
55 | }
56 |
57 | def isCurrent = currentNeuronId != None
58 |
59 | def chain(id: String, weight: SynapseTrait, threshold: Double, silenceIterations: Int) = {
60 | val n1 = current
61 | addStandard(id, threshold, silenceIterations)
62 | addSynapse(n1.id, id, weight)
63 | this
64 | }
65 |
66 | def chainDummy(id: String, weight: Double, hushValue: Int =defSilenceIterations) = {
67 | val n1 = current
68 | addDummy(id)
69 | addSynapse(n1.id, id, weight)
70 | this
71 | }
72 |
73 | def chainSilencingNeuron(id: String) = {
74 | val n1 = current
75 | addSilencingNeuron(id)
76 | addSynapse(n1.id, id, Silence())
77 | this
78 | }
79 |
80 | def addInput(id: String):NetBuilder = {
81 | addDummy(id)
82 | ins += id
83 | this
84 | }
85 |
86 | def initSilent(id: String): NetBuilder = {
87 | startSilent += id
88 | this
89 | }
90 |
91 | def initSilent(): NetBuilder = initSilent(current.id)
92 |
93 | def addMiddle(id: String,
94 | threshold: Double = defThreshold,
95 | silenceIterations: Int = defSilenceIterations):NetBuilder =
96 | addStandard(id, threshold, silenceIterations)
97 |
98 | def addMiddle():NetBuilder = addMiddle(generateId())
99 |
100 | def addSilencingNeuron(id: String) = {
101 | throwIfAlreadyExists(id)
102 | add(newNeuron(NeuronTypeSilencing(), id, 0.0))
103 | this
104 | }
105 |
106 | def addDummy(id: String) = {
107 | throwIfAlreadyExists(id)
108 | add(newNeuron(neuronType=NeuronTypeDummy(),
109 | id=id, threshold=0.0
110 | ))
111 | this
112 | }
113 |
114 | def addStandard(id: String,
115 | threshold: Double,
116 | silenceIterations: Int) = {
117 | debug(s"new neuron: $id")
118 | throwIfAlreadyExists(id)
119 | add(newNeuron(NeuronTypeStandard(), id, threshold, silenceIterations))
120 | this
121 | }
122 |
123 | def build(netName: String =netId) = {
124 | debug(this,s"build $netName")
125 | val net = NetRef(netName)
126 |
127 | debug(this, "building neurons")
128 | val nRefs = neurons.values.map( nd => {
129 | debug(this, s"creating $nd")
130 | nd.id -> createNeuronInNet(net, nd.withoutSynapses)
131 | }).toMap
132 |
133 | debug(this, "building synapses")
134 | nRefs.values.foreach(nRef => {
135 | val nsyns = synapses.getOrElse(nRef.id, Nil).map(sd => Synapse(nRefs(sd.neuronId), sd.weight))
136 | debug(this, s"building synapses for ${nRef.id}: ${nsyns.size}")
137 | nRef.setSynapses(nsyns)
138 | })
139 |
140 | startSilent.map(nRefs(_)).foreach( _.requestSilence() )
141 |
142 | debug(this, s"setting inputs: $ins")
143 | net.setInputs(ins.toList.sorted)
144 | debug(this, "done")
145 | NetWrapper(net)
146 | }
147 |
148 | def data = NetData(
149 | netId,
150 | neurons.values.map( n =>
151 | n.withSynapses(synapses.getOrElse(n.id, Nil).toList)
152 | ).toList.sortBy( _.id ),
153 | ins.toList.sorted,
154 | defThreshold,
155 | defSilenceIterations,
156 | defWeight
157 | )
158 |
159 | def set(data: NetData) = {
160 | netId = data.id
161 |
162 | clear()
163 |
164 | data.neurons.foreach( n => {
165 | neurons += (n.id -> n)
166 | val buffer = mutable.ListBuffer[SynapseData]()
167 | n.synapses.foreach( buffer += _ )
168 | synapses += ( n.id -> buffer )
169 | })
170 |
171 | ins ++= data.inputs
172 |
173 | defThreshold = data.threshold
174 | defSilenceIterations = data.silenceIterations
175 | defWeight = data.weight
176 |
177 | this
178 | }
179 |
180 | def netId(id: String):NetBuilder = {
181 | netId = id
182 | this
183 | }
184 |
185 | def clear() = {
186 | neurons.clear()
187 | synapses.clear()
188 | ins.clear()
189 | this
190 | }
191 |
192 | private def createNeuronInNet(net: NetRef, data: NeuronData) = await[NeuronRef](net, CreateNeuron(data))
193 |
194 | private val nextIndex = {
195 | var nextFreeIndex = 0L
196 | () => {
197 | val t = nextFreeIndex
198 | nextFreeIndex += 1L
199 | t
200 | }
201 | }
202 |
203 | private def newNeuron(neuronType: NeuronType,
204 | id: String,
205 | threshold: Double = defThreshold,
206 | silenceIterations: Int = defSilenceIterations) =
207 | NeuronData(id, threshold, silenceIterations, Nil, neuronType)
208 |
209 | private def add(n: NeuronData){
210 | neurons.put(n.id, n)
211 | currentNeuronId = Some(n.id)
212 | }
213 |
214 | private def throwIfAlreadyExists(id: String) = assert(!contains(id), s"There is already a neuron with id $id")
215 | }
216 |
217 | object NetBuilder {
218 | def apply():NetBuilder = new NetBuilder()
219 |
220 | def apply(name: String):NetBuilder = {
221 | val nb = new NetBuilder()
222 | nb.netId(name)
223 | nb
224 | }
225 |
226 | def apply(data: NetData):NetBuilder = {
227 | val builder = new NetBuilder()
228 | builder.set(data)
229 | }
230 | }
--------------------------------------------------------------------------------
/src/main/scala/anna/logger/LOG.scala:
--------------------------------------------------------------------------------
1 | package anna.logger
2 |
3 | import java.io.{PrintWriter, StringWriter}
4 | import anna.utils.Utils
5 |
6 | import anna.async.Neuron
7 |
8 | import scala.collection.mutable
9 |
10 | object LogLevel extends Enumeration {
11 | type LogLevel = Value
12 | val NONE, ERROR, DEBUG, INFO, COMMENT, ALL = Value
13 | }
14 |
15 | object LOG {
16 | var logLevel = LogLevel.DEBUG
17 | val outs = new mutable.ListBuffer[LogOutput]()
18 | var logTime = true
19 | var showLogLevel = false
20 | var trackAll = true
21 |
22 | private var fetchIterationFunction:Option[() => Int] = None
23 | def logIteration = fetchIterationFunction != None
24 | def startLoggingIterations(f:() => Int) = {
25 | fetchIterationFunction = Some(f)
26 | }
27 | def stopLogingIterations() = {
28 | fetchIterationFunction = None
29 | }
30 |
31 |
32 | private val trackedClasses = mutable.Set[String]()
33 |
34 | def track(c: Class[_]) = {
35 | val className = c.getName()
36 | trackedClasses += (if(className.endsWith("$")) className.substring(0,className.size-1) else className)
37 | trackAll = false
38 | }
39 |
40 | def stopTracking(c: Class[_]) = trackedClasses -= c.getName
41 |
42 | def isTracking(className: String):Boolean = trackedClasses.contains(className)
43 | def isTracking(c: Class[_]):Boolean = trackedClasses.contains(c.getName)
44 |
45 | def resetTracked() = {
46 | trackedClasses.clear
47 | trackAll = true
48 | }
49 |
50 | def resetOuts() = this.synchronized {
51 | outs.foreach( _.close )
52 | outs.clear
53 | outs += new SystemLogOutput()
54 | }
55 |
56 | def findOut(id: String) = this.synchronized {
57 | outs.find( _.id == id )
58 | }
59 |
60 | def addOut(out: LogOutput) = findOut(out.id) match {
61 | case Some(o) => o
62 | case None => outs += out; out
63 | }
64 |
65 | private val _allowedIds = mutable.Set[String]()
66 |
67 | def allow(id: String):Unit = _allowedIds += id
68 | def allow(ids: String*):Unit = ids.foreach( allow(_) )
69 | def allow[N <: Neuron](implicit n: N):Unit = allow(n.id)
70 |
71 | def allowedIds = _allowedIds.toSet
72 | def clearAllowedIds() = _allowedIds.clear()
73 | def removeAllowedId(id: String) = _allowedIds -= id
74 | def allowed(id: String):Boolean = _allowedIds.contains(id)
75 | def allowed[N <: Neuron](n: N):Boolean = allowed(n.id)
76 |
77 | def +=(str: String)(implicit n: Neuron) = log(str, n)
78 | def log(str: String, n: Neuron):Unit = if(allowed(n)) log(n.id + ": " + str, logLevel)
79 |
80 | def removeOut(out: LogOutput) = findOut(out.id) match{
81 | case None => false;
82 | case Some(o) => o.close(); outs -= o; true
83 | }
84 |
85 | def addLogToHTML(id: String) = addOut(new HTMLLogOutput(id))
86 |
87 | def addLogToFile(fileName: String) = addOut(new FileLogOutput(fileName))
88 |
89 | def addLogToStdout() = addOut(new StdoutLogOutput())
90 |
91 | def log(str: String, logLevel: LogLevel.Value):Unit = this.synchronized {
92 | if(logLevel > this.logLevel) return
93 | val sb = StringBuilder.newBuilder
94 | if(showLogLevel) sb ++= "level " + logLevel.toString() + ':'
95 | if(logTime) sb ++= "time " + dateTag + ':'
96 | if(logIteration) sb++= "iter " + fetchIterationFunction.get().toString + ':'
97 | sb ++= ">\t"
98 | sb ++= str
99 | outs.foreach{ _.println(sb.toString) }
100 | }
101 |
102 | private var offset:Option[Long] = None
103 |
104 | def timer(){
105 | offset = Some(System.currentTimeMillis())
106 | }
107 |
108 | def date(){
109 | offset = None
110 | }
111 |
112 | def time = offset match {
113 | case Some(t) => (System.currentTimeMillis() - t)
114 | case None => throw new IllegalArgumentException("Logger.time called with no timer set")
115 | }
116 |
117 | def timerSet = offset != None
118 |
119 | private def dateTag = offset match {
120 | case None => Utils.dateTag
121 | case Some(t) => (System.currentTimeMillis() - t).toString
122 | }
123 |
124 | def log(expr: => Boolean, str: String, logLevel: LogLevel.Value):Unit = if(expr) log(str, logLevel)
125 |
126 | def log(source: Any, str: String, logLevel: LogLevel.Value, neuronAllowed: Boolean =false):Unit =
127 | if(trackAll || neuronAllowed || isTracking(source.getClass))
128 | log(source.getClass.getName + "->" + str, logLevel)
129 |
130 |
131 | def stackToString(t: Throwable) = {
132 | val result = new StringWriter()
133 | t.printStackTrace(new PrintWriter(result))
134 | result.toString
135 | }
136 |
137 | def exception(t: Throwable): Unit = {
138 | log("Throwable object caught of type " + t.getClass().getName(), LogLevel.ERROR)
139 | log("message: " + t.getMessage, LogLevel.ERROR)
140 | log("cause: " + t.getCause, LogLevel.ERROR)
141 | log(stackToString(t), LogLevel.ERROR)
142 | throw new IllegalArgumentException(t.getMessage)
143 | }
144 |
145 | def error(t: Throwable): Unit = {
146 | error("Throwable object caught of type " + t.getClass().getName())
147 | error("message: " + t.getMessage())
148 | error("cause: " + t.getCause())
149 | error(stackToString(t))
150 | }
151 |
152 | def exception(str: String): Unit = {
153 | log(str, LogLevel.ERROR)
154 | throw new IllegalArgumentException(str)
155 | }
156 |
157 | def error(str: String): Unit = if (LogLevel.ERROR <= logLevel) log(str, LogLevel.ERROR)
158 | def debug(str: String): Unit = if (LogLevel.ERROR <= logLevel) log(str, LogLevel.DEBUG)
159 | def info(str: String): Unit = if (LogLevel.ERROR <= logLevel) log(str, LogLevel.INFO)
160 | def comment(str: String): Unit = if (LogLevel.ERROR <= logLevel) log(str, LogLevel.COMMENT)
161 |
162 | def exception(expr: => Boolean, str: String): Unit = {
163 | log(expr, str, LogLevel.ERROR)
164 | if(expr) throw new IllegalArgumentException(str)
165 | }
166 |
167 | def error(expr: => Boolean, str: String): Unit = if(LogLevel.ERROR <= logLevel) log(expr, str, LogLevel.ERROR)
168 | def debug(expr: => Boolean, str: String): Unit = if(LogLevel.ERROR <= logLevel) log(expr, str, LogLevel.DEBUG)
169 | def info(expr: => Boolean, str: String): Unit = if(LogLevel.ERROR <= logLevel) log(expr, str, LogLevel.INFO)
170 | def comment(expr: => Boolean, str: String): Unit = if(LogLevel.ERROR <= logLevel) log(expr, str, LogLevel.COMMENT)
171 |
172 | def exception(source: Any, str: String): Unit = {
173 | log(source, str, LogLevel.ERROR)
174 | throw new IllegalArgumentException(str)
175 | }
176 |
177 | def error[N <: Neuron](source: N, str: String): Unit = if(LogLevel.ERROR <= logLevel) log(source, str, LogLevel.ERROR, allowed(source))
178 | def debug[N <: Neuron](source: N, str: String): Unit = if(LogLevel.ERROR <= logLevel) log(source, str, LogLevel.DEBUG, allowed(source))
179 | def info[N <: Neuron](source: N, str: String): Unit = if(LogLevel.ERROR <= logLevel) log(source, str, LogLevel.INFO, allowed(source))
180 | def comment[N <: Neuron](source: N, str: String): Unit = if(LogLevel.ERROR <= logLevel) log(source, str, LogLevel.COMMENT, allowed(source))
181 |
182 | def error(source: Any, str: String): Unit = if(LogLevel.ERROR <= logLevel) log(source, str, LogLevel.ERROR)
183 | def debug(source: Any, str: String): Unit = if(LogLevel.ERROR <= logLevel) log(source, str, LogLevel.DEBUG)
184 | def info(source: Any, str: String): Unit = if(LogLevel.ERROR <= logLevel) log(source, str, LogLevel.INFO)
185 | def comment(source: Any, str: String): Unit = if(LogLevel.ERROR <= logLevel) log(source, str, LogLevel.COMMENT)
186 |
187 | def log(str: String): Unit = log(str, logLevel)
188 | def log(expr: => Boolean, str: String): Unit = log(expr, str, logLevel)
189 | def log(source: Any, str: String): Unit = log(source, str, logLevel)
190 |
191 | private var lastMeasure = System.currentTimeMillis
192 |
193 | def timer(tag: String): Long = {
194 | val t = System.currentTimeMillis()
195 | val result = t - lastMeasure
196 | log(tag + " took " + result + "ms.")
197 | lastMeasure = t
198 | result
199 | }
200 |
201 | def timer(source: Any, tag: String): Long = timer(source.getClass.getName + "->" + tag)
202 |
203 | def resetTimer(){
204 | lastMeasure = System.currentTimeMillis()
205 | }
206 |
207 | }
--------------------------------------------------------------------------------
/src/test/scala/anna/blocks/SOSWithBlocksSuite.scala:
--------------------------------------------------------------------------------
1 | package anna.blocks
2 |
3 | import anna.async.NetBuilder
4 | import anna.async.NetBuilderOps._
5 | import anna.logger.LOG
6 | import org.junit.Assert._
7 | import org.junit.Test
8 | import org.scalatest.junit.JUnitSuite
9 |
10 | /**
11 | * Created by gorywoda on 7/2/16.
12 | */
13 | class SOSWithBlocksSuite extends JUnitSuite {
14 |
15 | private def buildDot = {
16 | val dotBlockName = "DOT"
17 | val expectedDelay = 2
18 | val outputId = DelayGate.outputId(dotBlockName)
19 |
20 | val netWrapper = NetBuilder().addInput("in").delayGate(dotBlockName, expectedDelay).build()
21 | assertNotNull(netWrapper.find(outputId))
22 |
23 | (netWrapper, outputId)
24 | }
25 |
26 | @Test def shouldFireOnDot(): Unit = {
27 | var fired = false
28 | var iteration = 0
29 | val (netWrapper, outputId) = buildDot
30 |
31 | netWrapper.addAfterFire(outputId){
32 | iteration = netWrapper.iteration
33 | fired = true
34 | }
35 |
36 | netWrapper.iterateUntilCalm("1")
37 |
38 | assertTrue(fired)
39 | assertEquals(2, iteration-1)
40 |
41 | netWrapper.shutdown()
42 | }
43 |
44 | private def buildLine = {
45 | val lineBlockName = "LINE"
46 | val requiredSignals = 2
47 | val outputId = SignalSum.outputId(lineBlockName)
48 |
49 | val netWrapper = NetBuilder().addInput("in").signalSum(lineBlockName, 2).build()
50 | assertNotNull(netWrapper.find(outputId))
51 |
52 | (netWrapper, outputId)
53 | }
54 |
55 | @Test def shouldFireOnLine(): Unit = {
56 | var fired = false
57 |
58 | val (netWrapper, outputId) = buildLine
59 |
60 | netWrapper.addAfterFire(outputId){ fired = true }
61 |
62 | LOG.allow(outputId)
63 |
64 | netWrapper.iterateUntilCalm("1,1")
65 | assertTrue(fired)
66 |
67 | fired = false
68 | netWrapper.iterateUntilCalm("1")
69 | assertFalse(fired)
70 |
71 | netWrapper.shutdown()
72 | }
73 |
74 | private class TestResults {
75 | var dotFired = 0
76 | var lineFired = 0
77 | var sFired = 0
78 | var oFired = 0
79 | }
80 |
81 | private def buildDotLine = {
82 | val dotBlockName = "DOT"
83 | val expectedDelay = 2
84 | val dotOutputId = DelayGate.outputId(dotBlockName)
85 | val dotSilencingId = DelayGate.silencingId(dotBlockName)
86 | val dotInputId = DelayGate.inputId(dotBlockName)
87 | val dotMiddleId = DelayGate.middleId(dotBlockName)
88 |
89 | val lineBlockName = "LINE"
90 | val requiredSignals = 2
91 | val lineOutputId = SignalSum.outputId(lineBlockName)
92 | val lineSilencingId = SignalSum.silencingId(lineBlockName)
93 | val lineInputId = SignalSum.inputId(lineBlockName)
94 |
95 | val netWrapper =
96 | NetBuilder().addInput("in")
97 | .delayGate(dotBlockName, expectedDelay)
98 | .use("in").signalSum(lineBlockName, requiredSignals)
99 | .use(dotOutputId).silence(lineSilencingId)
100 | .use(lineOutputId).silence(dotSilencingId)
101 | .build()
102 |
103 | LOG.allow(dotOutputId, lineOutputId, dotSilencingId, dotInputId, dotMiddleId)
104 |
105 | val results = new TestResults;
106 |
107 | netWrapper.addAfterFire("in"){
108 | LOG.debug(s"${netWrapper.iteration}: Incoming!")
109 | }
110 |
111 | netWrapper.addAfterFire(dotOutputId){
112 | LOG.debug(s"${netWrapper.iteration}: Dot!")
113 | results.dotFired += 1
114 | }
115 |
116 | netWrapper.addAfterFire(lineOutputId){
117 | LOG.debug(s"${netWrapper.iteration}: Line!")
118 | results.lineFired += 1
119 | }
120 |
121 | (netWrapper, results)
122 | }
123 |
124 | @Test def shouldDotWithDotLine(): Unit = {
125 | val (netWrapper, results) = buildDotLine
126 |
127 | netWrapper.iterateUntilCalm("1")
128 |
129 | assertEquals(1, results.dotFired)
130 | assertEquals(0, results.lineFired)
131 |
132 | netWrapper.shutdown()
133 | }
134 |
135 | @Test def shouldLineWithDotLine(): Unit = {
136 | val (netWrapper, results) = buildDotLine
137 |
138 | netWrapper.iterateUntilCalm("1,1")
139 |
140 | assertEquals(0, results.dotFired)
141 | assertEquals(1, results.lineFired)
142 |
143 | netWrapper.shutdown()
144 | }
145 |
146 | @Test def shouldTwoDotsWithDotLine(): Unit = {
147 | val (netWrapper, results) = buildDotLine
148 |
149 | netWrapper.iterateUntilCalm("1,0,0,0,1")
150 |
151 | assertEquals(2, results.dotFired)
152 | assertEquals(0, results.lineFired)
153 |
154 | netWrapper.shutdown()
155 | }
156 |
157 | @Test def shouldLineAndDotWithDotLine(): Unit = {
158 | val (netWrapper, results) = buildDotLine
159 |
160 | netWrapper.iterateUntilCalm("1,1,0,0,1")
161 |
162 | assertEquals(1, results.dotFired)
163 | assertEquals(1, results.lineFired)
164 |
165 | netWrapper.shutdown()
166 | }
167 |
168 | @Test def shouldDotAndLineWithDotLine(): Unit = {
169 | val (netWrapper, results) = buildDotLine
170 |
171 | netWrapper.iterateUntilCalm("1,0,0,0,1,1")
172 |
173 | assertEquals(1, results.dotFired)
174 | assertEquals(1, results.lineFired)
175 |
176 | netWrapper.shutdown()
177 | }
178 |
179 | @Test def shouldThreeDotsWithDotLine(): Unit = {
180 | val (netWrapper, results) = buildDotLine
181 |
182 | netWrapper.iterateUntilCalm("1,0,0,0,1,0,0,0,1")
183 |
184 | assertEquals(3, results.dotFired)
185 | assertEquals(0, results.lineFired)
186 |
187 | netWrapper.shutdown()
188 | }
189 |
190 | @Test def shouldThreeLinesWithDotLine(): Unit = {
191 | val (netWrapper, results) = buildDotLine
192 |
193 | netWrapper.iterateUntilCalm("1,1,0,0,1,1,0,0,1,1")
194 |
195 | assertEquals(0, results.dotFired)
196 | assertEquals(3, results.lineFired)
197 |
198 | netWrapper.shutdown()
199 | }
200 |
201 | private def buildSO(outputBuffer:Option[StringBuilder] = None) = {
202 | val dotBlockName = "DOT"
203 | val dotExpectedDelay = 2
204 | val dotOutputId = DelayGate.outputId(dotBlockName)
205 | val dotHushId = DelayGate.silencingId(dotBlockName)
206 | val dotInputId = DelayGate.inputId(dotBlockName)
207 | val dotMiddleId = DelayGate.middleId(dotBlockName)
208 |
209 | val lineBlockName = "LINE"
210 | val lineRequiredSignals = 2
211 | val lineOutputId = SignalSum.outputId(lineBlockName)
212 | val lineHushId = SignalSum.silencingId(lineBlockName)
213 | val lineInputId = SignalSum.inputId(lineBlockName)
214 |
215 | val sBlockName = "S"
216 | val sRequiredSignals = 3
217 | val sOutputId = SignalSum.outputId(sBlockName)
218 | val sHushId = SignalSum.silencingId(sBlockName)
219 | val sInputId = SignalSum.inputId(sBlockName)
220 |
221 | val oBlockName = "O"
222 | val oRequiredSignals = 3
223 | val oOutputId = SignalSum.outputId(oBlockName)
224 | val oHushId = SignalSum.silencingId(oBlockName)
225 | val oInputId = SignalSum.inputId(oBlockName)
226 |
227 | val netWrapper =
228 | NetBuilder().addInput("in")
229 | .delayGate(dotBlockName, dotExpectedDelay)
230 | .use("in").signalSum(lineBlockName, lineRequiredSignals)
231 | .use(dotOutputId).silence(lineHushId)
232 | .use(lineOutputId).silence(dotHushId)
233 | .use(dotOutputId).signalSum(sBlockName, sRequiredSignals)
234 | .use(lineOutputId).signalSum(oBlockName, oRequiredSignals)
235 | .use(sOutputId).silence(oHushId)
236 | .use(oOutputId).silence(sHushId)
237 | .build()
238 |
239 |
240 | LOG.allow(dotOutputId, lineOutputId, dotHushId, lineHushId, dotInputId, lineInputId, dotMiddleId)
241 |
242 | val results = new TestResults
243 |
244 | netWrapper.addAfterFire("in"){
245 | LOG.debug(s"${netWrapper.iteration}: Incoming!")
246 | }
247 |
248 | netWrapper.addAfterFire(dotOutputId){
249 | LOG.debug(s"${netWrapper.iteration}: Dot!")
250 | results.dotFired += 1
251 | }
252 |
253 | netWrapper.addAfterFire(lineOutputId){
254 | LOG.debug(s"${netWrapper.iteration}: Line!")
255 | results.lineFired += 1
256 | }
257 |
258 | netWrapper.addAfterFire(sOutputId){
259 | LOG.debug(s"${netWrapper.iteration}: S!")
260 | results.sFired += 1
261 | outputBuffer match {
262 | case Some(buffer) => buffer.append('S')
263 | case None =>
264 | }
265 | }
266 |
267 | netWrapper.addAfterFire(oOutputId){
268 | LOG.debug(s"${netWrapper.iteration}: O!")
269 | results.oFired += 1
270 | outputBuffer match {
271 | case Some(buffer) => buffer.append('O')
272 | case None =>
273 | }
274 | }
275 |
276 | (netWrapper, results)
277 | }
278 |
279 | @Test def shouldSWithSO(): Unit = {
280 | val (netWrapper, results) = buildSO()
281 |
282 | netWrapper.iterateUntilCalm("1,0,0,0,1,0,0,0,1")
283 |
284 | assertEquals(3, results.dotFired)
285 | assertEquals(0, results.lineFired)
286 | assertEquals(1, results.sFired)
287 | assertEquals(0, results.oFired)
288 |
289 | netWrapper.shutdown()
290 | }
291 |
292 | @Test def shouldOWithSO(): Unit = {
293 | val (netWrapper, results) = buildSO()
294 |
295 | netWrapper.iterateUntilCalm("1,1,0,0,1,1,0,0,1,1")
296 |
297 | assertEquals(0, results.dotFired)
298 | assertEquals(3, results.lineFired)
299 | assertEquals(0, results.sFired)
300 | assertEquals(1, results.oFired)
301 |
302 | netWrapper.shutdown()
303 | }
304 |
305 | @Test def shouldSOWithSO(): Unit = {
306 | val (netWrapper, results) = buildSO()
307 |
308 | netWrapper.iterateUntilCalm("1,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0,1,1,0,0,1,1")
309 |
310 | assertEquals(3, results.dotFired)
311 | assertEquals(3, results.lineFired)
312 | assertEquals(1, results.sFired)
313 | assertEquals(1, results.oFired)
314 |
315 | netWrapper.shutdown()
316 | }
317 |
318 | /* This is the test which proves it all works :) */
319 |
320 | @Test def shouldSOSWithSO(): Unit = {
321 | val buffer = StringBuilder.newBuilder
322 | val (netWrapper, results) = buildSO(Some(buffer))
323 |
324 | netWrapper.iterateUntilCalm("1,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,0,0,0,1,0,0,0,1")
325 |
326 | assertEquals(6, results.dotFired)
327 | assertEquals(3, results.lineFired)
328 | assertEquals(2, results.sFired)
329 | assertEquals(1, results.oFired)
330 |
331 | assertEquals("SOS", buffer.toString())
332 |
333 | netWrapper.shutdown()
334 | }
335 |
336 | }
337 |
--------------------------------------------------------------------------------
/src/main/scala/anna/Commands.scala:
--------------------------------------------------------------------------------
1 | package anna
2 |
3 | import anna.async.{NetBuilder, NetWrapper}
4 | import anna.data.NetData
5 | import anna.logger.LOG._
6 | import anna.async.NetBuilderOps._
7 | import anna.blocks.{DelayGate, SignalSum}
8 | import anna.logger.LOG
9 | /**
10 | * Created by gorywoda on 06.06.15.
11 | */
12 |
13 |
14 | object Commands {
15 |
16 | private var netWrapperOpt: Option[NetWrapper] = None
17 |
18 | val DELAY_GATE = "Delay Gate"
19 | val SIGNAL_SUM = "Signal Sum"
20 | val DOT_AND_LINE = "Dot & Line"
21 | val SOS = "SOS"
22 |
23 | def delayGate(delay: Int) = NetBuilder().netId(DELAY_GATE).addInput("IN").delayGate("DG", delay).data
24 |
25 | def signalSum(requiredSignals: Int) = NetBuilder().netId(SIGNAL_SUM).addInput("IN").signalSum("SS", requiredSignals).data
26 |
27 | lazy val dotAndLine =
28 | NetBuilder().netId(DOT_AND_LINE)
29 | .addInput("IN").delayGate("DOT", 2)
30 | .use("IN").signalSum("LINE", 2)
31 | .use(DelayGate.outputId("DOT")).silence(SignalSum.silencingId("LINE"))
32 | .use(SignalSum.outputId("LINE")).silence(DelayGate.silencingId("DOT"))
33 | .data
34 |
35 | lazy val sos = {
36 | val dotBlockName = "DOT"
37 | val dotExpectedDelay = 2
38 | val dotOutputId = DelayGate.outputId(dotBlockName)
39 | val dotSilencingId = DelayGate.silencingId(dotBlockName)
40 | val dotInputId = DelayGate.inputId(dotBlockName)
41 | val dotMiddleId = DelayGate.middleId(dotBlockName)
42 |
43 | val lineBlockName = "LINE"
44 | val lineRequiredSignals = 2
45 | val lineOutputId = SignalSum.outputId(lineBlockName)
46 | val lineSilencingId = SignalSum.silencingId(lineBlockName)
47 | val lineInputId = SignalSum.inputId(lineBlockName)
48 |
49 | val sBlockName = "S"
50 | val sRequiredSignals = 3
51 | val sOutputId = SignalSum.outputId(sBlockName)
52 | val sSilencingId = SignalSum.silencingId(sBlockName)
53 | val sInputId = SignalSum.inputId(sBlockName)
54 |
55 | val oBlockName = "O"
56 | val oRequiredSignals = 3
57 | val oOutputId = SignalSum.outputId(oBlockName)
58 | val oSilencingId = SignalSum.silencingId(oBlockName)
59 | val oInputId = SignalSum.inputId(oBlockName)
60 |
61 | NetBuilder().netId(SOS)
62 | .addInput("IN").delayGate(dotBlockName, dotExpectedDelay)
63 | .use("IN").signalSum(lineBlockName, lineRequiredSignals)
64 | .use(dotOutputId).silence(lineSilencingId)
65 | .use(lineOutputId).silence(dotSilencingId)
66 | .use(dotOutputId).signalSum(sBlockName, sRequiredSignals)
67 | .use(lineOutputId).signalSum(oBlockName, oRequiredSignals)
68 | .use(sOutputId).silence(oSilencingId)
69 | .use(oOutputId).silence(sSilencingId)
70 | .data
71 | }
72 |
73 | private def wrapper = netWrapperOpt.getOrElse(throw new IllegalArgumentException("NetWrapper not set"))
74 |
75 | private def addAfterFireTrigger(neuronId: String)(f: => Any) = wrapper.addAfterFire(neuronId)(f)
76 | private def addSilenceRequestedTrigger(neuronId: String)(f: => Any) = wrapper.addSilenceRequested(neuronId)(f)
77 | private def addSignalIgnoredTrigger(neuronId: String)(f: => Any) = wrapper.addSignalIgnored(neuronId)(f)
78 |
79 | val outputBuffer = StringBuilder.newBuilder
80 | def output = outputBuffer.toString
81 | def clearOutput() = outputBuffer.clear()
82 |
83 | def initializeNetwork(netData: NetData) = {
84 | LOG.timer()
85 | clearOutput()
86 | netWrapperOpt = Some(NetBuilder().set(netData).build())
87 | LOG.clearAllowedIds()
88 | }
89 |
90 | private def setupDelayGate(netData: NetData) = {
91 | initializeNetwork(netData)
92 |
93 | addAfterFireTrigger(DelayGate.outputId("DG")){
94 | LOG.debug("Pushing '1' into the output")
95 | outputBuffer.append('1')
96 | }
97 |
98 | LOG.allow(
99 | "IN",
100 | DelayGate.inputId("DG"),
101 | DelayGate.silencingId("DG"),
102 | DelayGate.middleId("DG"),
103 | DelayGate.outputId("DG")
104 | )
105 | }
106 |
107 | private def setupSignalSum(netData: NetData) = {
108 | initializeNetwork(netData)
109 |
110 | addAfterFireTrigger(SignalSum.outputId("SS")){
111 | LOG.debug("Pushing '1' into the output")
112 | outputBuffer.append('1')
113 | }
114 |
115 | LOG.allow(
116 | "IN",
117 | SignalSum.inputId("SS"),
118 | SignalSum.silencingId("SS"),
119 | SignalSum.outputId("SS")
120 | )
121 | }
122 |
123 | private def setupDotAndLine(netData: NetData) = {
124 | initializeNetwork(netData)
125 |
126 | val dotInputId = DelayGate.inputId("DOT")
127 | val dotMiddleId = DelayGate.middleId("DOT")
128 | val dotSilencingId = DelayGate.silencingId("DOT")
129 | val dotOutputId = DelayGate.outputId("DOT")
130 |
131 | addAfterFireTrigger(dotOutputId){
132 | LOG.debug("Pushing '.' into the output")
133 | outputBuffer.append('.')
134 | }
135 |
136 | val lineInputId = SignalSum.inputId("LINE")
137 | val lineSilencingId = SignalSum.silencingId("LINE")
138 | val lineOutputId = SignalSum.outputId("LINE")
139 |
140 | addAfterFireTrigger(lineOutputId){
141 | LOG.debug("Pushing '-' into the output")
142 | outputBuffer.append('-')
143 | }
144 |
145 | LOG.allow(
146 | "IN",
147 | dotInputId, dotMiddleId, dotOutputId, dotSilencingId,
148 | lineInputId, lineOutputId, lineSilencingId
149 | )
150 | }
151 |
152 | private def setupSOS(netData: NetData) = {
153 | initializeNetwork(netData)
154 |
155 | val dotInputId = DelayGate.inputId("DOT")
156 | val dotMiddleId = DelayGate.middleId("DOT")
157 | val dotSilencingId = DelayGate.silencingId("DOT")
158 | val dotOutputId = DelayGate.outputId("DOT")
159 |
160 | val lineInputId = SignalSum.inputId("LINE")
161 | val lineSilencingId = SignalSum.silencingId("LINE")
162 | val lineOutputId = SignalSum.outputId("LINE")
163 |
164 | val sInputId = SignalSum.inputId("S")
165 | val sSilencingId = SignalSum.silencingId("S")
166 | val sOutputId = SignalSum.outputId("S")
167 |
168 | addAfterFireTrigger(sOutputId){
169 | LOG.debug("Pushing 'S' into the output")
170 | outputBuffer.append('S')
171 | }
172 |
173 | val oInputId = SignalSum.inputId("O")
174 | val oSilencingId = SignalSum.silencingId("O")
175 | val oOutputId = SignalSum.outputId("O")
176 |
177 | addAfterFireTrigger(oOutputId){
178 | LOG.debug("Pushing 'O' into the output")
179 | outputBuffer.append('O')
180 | }
181 |
182 | LOG.allow(
183 | "IN",
184 | dotInputId, dotMiddleId, dotOutputId, dotSilencingId,
185 | lineInputId, lineOutputId, lineSilencingId,
186 | sInputId, sOutputId, sSilencingId,
187 | oInputId, oOutputId, oSilencingId
188 | )
189 | }
190 |
191 | def setup(netData: NetData) = {
192 | shutdown()
193 | netData.id match {
194 | case DELAY_GATE => setupDelayGate(netData)
195 | case SIGNAL_SUM => setupSignalSum(netData)
196 | case DOT_AND_LINE => setupDotAndLine(netData)
197 | case SOS => setupSOS(netData)
198 | case other => error(s"netId not recognized: $other")
199 | }
200 | }
201 |
202 | private def resetBeforeWork(sequence: String) = {
203 | wrapper.reset()
204 | wrapper.resetIterations()
205 | clearOutput()
206 | LOG.timer()
207 | LOG.startLoggingIterations(() => wrapper.iteration)
208 | sequence.replaceAll(",","").toCharArray.mkString(",") // just to make sure
209 | }
210 |
211 | def send(sequence: String):Unit = {
212 | val validSequence = resetBeforeWork(sequence)
213 | wrapper.iterateUntilCalm(validSequence)
214 | Thread.sleep(Context().iterationTime)
215 | }
216 |
217 | def send(sequence: String, repeatTimes: Int):Unit = {
218 | val validSequence = resetBeforeWork(sequence)
219 | for(i <- 1 to repeatTimes){
220 | wrapper.iterateUntilCalm(validSequence)
221 | }
222 | Thread.sleep(Context().iterationTime)
223 | }
224 |
225 | def shutdown() = netWrapperOpt match {
226 | case Some(netWrapper) => netWrapper.shutdown()
227 | case _ =>
228 | }
229 |
230 | def neuronsIds = wrapper.neuronIds
231 | def maxIterations = Context().maxRunIterations
232 | def setMaxIterations(maxIterations: Int) = {
233 | Context.withMaxRunIterations(maxIterations)
234 | }
235 |
236 | def print(netData: NetData):Unit = LOG.debug(netData.toJson)
237 |
238 | def help = println(
239 | s"""
240 | | You can use pre-constructed networks:
241 | | 1. sos
242 | | 2. dotAndLine
243 | | and utility methods for network construction:
244 | | 3. delayGate(delay: Int)
245 | | 4. signalSum(requestedSum: Int)
246 | | All four give you an object of the type NetData which you can then pass to the setup method:
247 | | > setup(sos)
248 | | > setup(dotAndLine)
249 | | > setup(delayGate(2))
250 | | > setup(signalSum(3))
251 | | This will build the network and display the list of neuron ids.
252 | |
253 | | The setup method works only for these four networks. If you want to build one of your own, please look into
254 | | the code to learn how to use NetBuilder and then call initializeNetwork(netData: NetData). Please note that
255 | | the setup method adds triggers to output neurons (different for each network). If you build a network by hand,
256 | | you have to do this by hand as well.
257 | |
258 | | The output neurons for the networks are, respectively:
259 | | 1. sos: S2 (sends 'S' to the output) and O2 (sends 'O')
260 | | 2. dotAndLine: DOT3 (sends '.') and LINE2 (sends '-')
261 | | 3. delayGate(_): DG3 (sends '1')
262 | | 4. signalSum(_): SS2 (sends '1')
263 | |
264 | | You can also print the JSON form of each network, e.g.:
265 | | > print(delayGate(2))
266 | |
267 | | After the setup, you can send input vectors to the network and see how it handles them. Since all four networks
268 | | have only one input neuron, you can use a utility method and send the whole input vectors' sequence in form
269 | | of a string, eg.:
270 | | > send("100010001000110011001100100010001000")
271 | | The string will be parsed into signals which will be sent to the input neuron and from there to other neurons
272 | | in the network. Then you can see the output by typing:
273 | | > output
274 | |
275 | | If you want to send longer input sequences, please modify the maximum number of iterations:
276 | | > setMaxIterations(_)
277 | | The variable was introduced in order to prevent infinite processing which is possible with customized networks.
278 | | The default number is $maxIterations. You can check it by typing:
279 | | > maxIterations
280 | |
281 | | During the processing the console will display logs from the neurons, about what signals they received and sent,
282 | | their internal state, etc. You can control logs from which neurons you want to see, by typing:
283 | | > LOG.allow(neuronId: String)
284 | | > LOG.removedAllowedId(neuronId: String)
285 | | > LOG.clearAllowedIds()
286 | | Do that between the setup and the send methods. The next setup will reset the list.
287 | """.stripMargin)
288 |
289 | }
290 |
--------------------------------------------------------------------------------
/doc/signal_sum.graphml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | IN
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | OUT
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 1
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | Signal Sum
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | SS1
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | SS2
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | w(IN, SS1)
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 | w(SS2, OUT)
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 | w(SS1, SS2)
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
--------------------------------------------------------------------------------
/doc/DOT.graphml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | IN
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 1
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | DOT
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | DOT2
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | DOT3
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | DOT1
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | DOTs
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 | dot
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
--------------------------------------------------------------------------------
/doc/neuron.graphml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 1
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | Neuron
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | Buffer
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | threshold
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | activation function
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | outputs
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | inputs
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | Silencing neuron
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 | Silence request
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 | reset
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
--------------------------------------------------------------------------------
/doc/silenceable_signal_sum.graphml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | IN
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | OUT
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 1
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | Silenceable Signal Sum
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | SS1
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | SS2
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | SSs
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | Silence
139 | request
140 | sender
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 | w(IN, SS1)
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 | w(SS2, OUT)
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 | w(SS1, SS2)
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 | Silence request
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 | Silence req. (1)
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 | Silence req. (1)
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
--------------------------------------------------------------------------------