├── 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 | --------------------------------------------------------------------------------