diff --git a/history/005_letter_test/src/main/java/com/github/drinkjava2/frog/brain/Cell.java b/history/005_letter_test/src/main/java/com/github/drinkjava2/frog/brain/Cell.java new file mode 100644 index 0000000..7dcb8ba --- /dev/null +++ b/history/005_letter_test/src/main/java/com/github/drinkjava2/frog/brain/Cell.java @@ -0,0 +1,188 @@ +/* + * Copyright 2018 the original author or authors. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by + * applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS + * OF ANY KIND, either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ +package com.github.drinkjava2.frog.brain; + +import java.util.Arrays; + +import com.github.drinkjava2.frog.util.ColorUtils; +import com.github.drinkjava2.frog.util.RandomUtils; + +/** + * Cell is the smallest unit of brain space, a Cell can have many actions and + * photons and holes and relations + * + * Cell是脑的最小空间单元,可以存在多个行为(由organs中的器官代号来表示)和光子(Photon)和洞(Hole)和关联(Relation),脑是由frog中的cells三维数组组 + * 成,但不是每一维都初始化过。 + * + * Cell原则上只有数据,应该把所有方法移出去,放到具体的器官行为中,见CellActions类,但暂时图省事,在Cell里放一些常用方法 + * + * Jelly或MoveJelly器官有一种特殊行为,它可以产生动态触突效果,可以把具备动态触突的神经元比作一个果冻,光子来了,在上面撞了一个坑(hole)并损失能量, + * 如果来的多,或者速度快(能量大),一部分的光子就被从果冻的另一头撞出去了(光直线传播,寻找下一个神经元,增加信息存储单元,实现体全息存贮)。如果在另一个角度又 + * 来了新的光子,同样的过程在发生,只不过在撞击的过程中,以前被撞出的坑里有可能被撞出光子来,沿着撞击坑的路径直线逆向返回(即波的逆向成像,两个撞击事件,如果 + * 在短期内同时发生,或长期反复发生,就形成比较稳定的关联,这个与神经网络的hebb规则相符合,以后在这个基础上,看看能不能将深度学习的分层结构借签进来,用图形化 + * 表示,并有可能是无级分层的,层与层之前没有明显界限。 + * + * @author Yong Zhu + * @since 1.0 + */ +public class Cell { + public int x; + public int y; + public int z; + + public Cell(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** Active of current cell */ + public boolean hasInput = false; // 这个细胞是否有外界信号(如光、声音)输入 + + public int[] organs = null; //// 每个Cell可以被多个Organ登记,这里保存organ在蛋里的序号 + + public Photon[] photons = null;// 光子 + + public Hole[] holes = null;// 洞(即动态突触),洞由光子产生,洞由时间抹平 + + public Relation[] relations = null;// 洞的关联关系 + + public int color;// Cell的颜色取最后一次被添加的光子的颜色,颜色不重要,但能方便观察 + + public int photonQty = 0; // 这个细胞里当前包含的光子总数 + + public int photonSum = 0; // 这个细胞里曾经收到的光子总数 + + public void regOrgan(int orgNo) {// 每个Cell可以被多个Organ登记,通常只在青蛙初始化器官时调用这个方法 + if (organs == null) + organs = new int[] {}; + organs = Arrays.copyOf(organs, organs.length + 1); + organs[organs.length - 1] = orgNo; + } + + public void addPhoton(Photon p) {// 每个cell可以存在多个光子 + if (p == null) + return; + photonQty++; + photonSum++; + color = p.color; // Cell的颜色取最后一次被添加的光子的颜色 + if (photons == null) { + photons = new Photon[] { p };// 创建数组 + return; + } else + for (int i = 0; i < photons.length; i++) { // 找空位插入,尽量重复利用内存 + if (photons[i] == null) { + photons[i] = p; + return; // 如找到就插入,返回 + } + } + photons = Arrays.copyOf(photons, photons.length + 1);// 否则追加新光子到未尾 + photons[photons.length - 1] = p; + } + + public void digHole(Photon p) {// 根据光子来把洞挖大一点,同时,如果有洞和它年龄相近,将把它们绑定起来,如果已有绑定的洞,有可能在绑定的洞里撞出光子来 + if (p == null || p.isBackway())// 反向的光子不参与挖坑 + return; + if (holes == null) {// 如果没有坑,就新挖一个 + holes = new Hole[] { new Hole(p) }; // 新挖的坑不参与绑定 + return; + } + + if (RandomUtils.percent(10)) // 这个机率纯粹是为了减少光子数,增加运行速度 + for (int i = 0; i < holes.length; i++) { // 这部分很关键,光子如果与坑同向或角度相近,会在与坑绑定的坑上撞出新的光子反向飞回,注意只针对绑定的坑 + Hole h = holes[i]; + if (h != null) { + float r = h.angleCompare(p); + if (r < 0.05) { + if (RandomUtils.percent(h.size)) + createBackPhoton(h); // 产生光子的机率与洞的大小有关 + } else if (r > 0.1 && r < 0.5) { // 这个叫侧抑制,角度不等但相近的光子射过来会使洞变小 + h.size -= 10; + if (h.size < 10) + h.size = 10; + } + } + } + + Hole found = null; + for (int i = 0; i < holes.length; i++) { // 先看看已存在的洞是不是与光子同向,是的话就把洞挖大一点 + Hole h = holes[i]; + if (h != null && h.ifSimilarWay(p)) { // 找到了与入射光子同向的洞,实际上就是同一个波源发来的 + found = h; + h.size += 10; + h.age = 0; // 为0表示这个洞被重新激活,可以参与绑定 + if (h.size > 100) + h.size = 100; + break; + } + } + + if (found != null) { // 如果第二次扩洞,且光子和洞不是同一个器官产生的,这时可以把这个洞和其它洞关联起来了 + for (Hole hole : holes) { + if (hole != found && found.organNo != hole.organNo && (Math.abs(found.age - hole.age) < 80)) {// TODO:不应用固定值 + bind(found, hole); + } + } + } + + if (found == null) {// 如果还没有找到旧坑,只好挖一个新坑到未尾 + holes = Arrays.copyOf(holes, holes.length + 1); + holes[holes.length - 1] = new Hole(p); + } + } + + private void createBackPhoton(Hole h) { // 根据给定的洞,把所有与它绑定的洞上撞出光子来 + if (relations == null) + return; + for (Relation r : relations) { + Hole f = null; + if (h.equals(r.h1)) + f = r.h2;// h2与h是一对绑定的 + else if (h.equals(r.h2)) + f = r.h1; // h1与h是一对绑定的 + if (f != null) { + Photon back = new Photon(-1, ColorUtils.RED, f.x, f.y, f.z, -f.mx, -f.my, -f.mz);// 生成反向的光子 + addPhoton(back); + // energy -= 90; + } + } + } + + public void bind(Hole a, Hole b) {// 将两个坑绑定,以后只要有一个坑激活,另一个坑也会产生出光子 + if (relations == null) { + relations = new Relation[] { new Relation(a, b) }; + return; + } + for (Relation r : relations) {// 先看看是不是已绑过,绑过就把强度乘1.5 + if ((r.h1 == a && r.h2 == b) || (r.h2 == a && r.h1 == b)) { + r.strength *= 1.5; + if (r.strength > 100000) // TODO: strength要有遗忘机制 + r.strength = 100000; + return; + } + } + relations = Arrays.copyOf(relations, relations.length + 1); + relations[relations.length - 1] = new Relation(a, b); + } + + public void removePhoton(int i) {// 删第几个光子 + if (photons[i] != null) { + photons[i] = null; + photonQty--; + } + } + + public void deleteAllPhotons() { + photons = null; + photonQty = 0; + } + +}