This commit is contained in:
2025-10-09 16:09:10 +08:00
parent 27faac8cd4
commit 0c5830c471

View File

@@ -0,0 +1,132 @@
/*
* 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.gitee.drinkjava2.frog.brain;
import java.io.Serializable;
import com.gitee.drinkjava2.frog.Animal;
import com.gitee.drinkjava2.frog.util.RandomUtils;
/**
* Line is line connect 2 cells, may have many attributes
* Line是一个随机连接两个脑细胞的连线连线有多种不同属性不同的属性有不同的静态、动态参数以及不同的行为
* 因为Line数量太多了对象的创建消毁太消耗资源为了节约内存和提高速度把Line设计成一个虚拟逻辑对象不会实际创建它的实例。所以目前这个Line里只保留静态方法。
* Line的动态参数保存在Animal.lines数列中每个元素是一个整数数组分别为 type,x1,y1,z1,x2,y2,z2,dres解释见下
* type: 线的类型同样类型的线它的常量参数是相同的常量参数保存在Animal.consts数组中起点为type*countsQTY
* x1,y1,z1为源x2,y2,z2为目的。能量永远是从源到目流动与能量的正负无关。要实现双向能量流动必须建立两根线。
* dres dynamic resistance 动态电阻值动态电阻率敏感度大于0时电阻在重复信号刺激下会变小这样就可用电阻来作为记忆元件这个值是个有状态的值每根线条都要单独记录不像其它参数是个常量
* Line的常量参数都取值在0~1之间保存在Animal.consts数组中起点为type*countsQTY通过这种方法可以保证相同type的线它的常量参数相同(以便于将来移植到分裂算法上),参数依次为:
* res: resistance, 常量电阻,即通常说的权重, 0时为断路1时为全通因为no相同的line常量相同所以一条线只需要记录no不需要记录每个常量参数
* drs: dynamic resistance sensitivity 动态电阻率敏感度0时电阻率不会变动 1时电阻率会随信号重复立即变到1(全通),其它值处于两者之间
* ovfoverflow 常量溢流阀值, 当传递的能量大于阀值时,高于阀值的部分能量传递
* ovb: overflow break 常量溃坝阀值, 当传递的能量大于阀值时, 能量全部传递
* not: not logic 反相器,如>0.5, 将会对通过的能量反相,即乘以-1
* mul: multiple 乘法器会对通过的能量值乘以一个倍数0为11时为10倍
* min: minus 扣能器, 会对细胞的能量扣除一部分0为01时为1直到细胞能量为0
* add: add 加能器会对细胞能量增加一部分0为01时为1,直到细胞能量为1
* 常量参数是叠加的一个Line可以同时具有常量电阻、动态电阻、溢流阀、反相器、扣能器等属性优先级暂以上面顺序为准
* 题外话:
* 目前版本Line的数量少可以为每个Line的参数在Animal里分配一个数组空间。当数量多后将来打算可能用分裂算法来代替随机连线算法分裂算法主要优点一是压缩率高二是
* 树形结构比较合理,节点的等级不同,一个节点的变异可能造成整个子树的增删,随机连线算法做不到这一点,所有节点都是同等级的,无法应对环境突变情况
* 如果将来采用分裂算法Line的所有动态、静态参数都要想办法挂靠在Cell上比如源、目的坐标要改成相对当前细胞的偏移量这样就把同一类细胞的绝对坐标动态参数变成了相对坐标的静态常量。
* Line至少会有个动态电阻当前dres值这是个有状态的值无法变成常量将来也要想办法把每根线的这个值挂靠在Cell上。反正目标就是消除Line这个对象的生成让它变成一个虚拟对象以节约内存和提高速度。
*
* @author Yong Zhu
* @since 1.0
*/
public class Line implements Serializable {
private static final long serialVersionUID = 1L;
public static int countsQTY = 8; // res, drs, ovf , ovb, not ...等常量参数
public static void randomAddorRemoveLine(Animal a) {//对animal的线条个数进行随机增减。线条参数的变异不在这个方法中而是在常量变异见anmial的constMutate方法
if (RandomUtils.percent(10f)) { // 这两行随机加减Line
int i = RandomUtils.nextInt(Genes.dots.size());
Object[] d1 = Genes.dots.get(i);
i = RandomUtils.nextInt(Genes.dots.size());
Object[] d2 = Genes.dots.get(i);
a.lines.add(new int[] { RandomUtils.nextInt(Animal.CountsQTY / countsQTY), (int) d1[1], (int) d1[2], (int) d1[3], (int) d2[1], (int) d2[2], (int) d2[3], 5000 });
}
if (!a.lines.isEmpty() && RandomUtils.percent(10f))
a.lines.remove(RandomUtils.nextInt(a.lines.size()));
}
// ========= active方法在每个主循环都会调用调用animal所有Line的行为这是个重要方法 ===========
public static void active(Animal a, int step) {//
for (int[] l : a.lines) {
int start = l[0] * countsQTY; //start是line在常数数组的起始点
int x1 = l[1];
int y1 = l[2];
int z1 = l[3];
int x2 = l[4];
int y2 = l[5];
int z2 = l[6];
// int dres = l[7]; //dres取值0~10000对应电阻率0~1
float res = a.consts[start++]; //resistance, 常量电阻,即通常说的权重, 0时为断路1时为全通
// float drs = a.consts[start++]; //dynamic resistance sensitivity 动态电阻率敏感度0时电阻率不会变动 1时电阻率会随信号重复立即变到1(全通),其它值处于两者之间
//float ovf = a.consts[start++]; //overflow 常量溢流阀值, 当传递的能量大于阀值时,高于阀值的部分能量传递
//float ovb = a.consts[start++]; //overflow break 常量溃坝阀值, 当传递的能量大于阀值时, 能量全部传递
float not = a.consts[start++]; //not logic 反相器,如>0.5, 将会对通过的能量反相,即乘以-1
// float mul = a.consts[start++]; //乘法器会对通过的能量值乘以一个倍数0为11时为10倍
// float min = a.consts[start++]; //扣能器, 会对细胞的能量扣除一部分0为01时为1直到细胞能量为0
// float add = a.consts[start++]; //加能器会对细胞能量增加一部分0为01时为1
float e = a.getEng(x1, y1, z1);
if (e < 0.1f)
continue;
if (e > 0.3f) {
// if (min > 0.1) { //扣能器
// float newSrcEng = a.getEng(x1, y1, z1) - min;
// a.setEng(x1, y1, z1, newSrcEng);
// }
// if (add > 0.1) { //加能器
// float newSrcEng = a.getEng(x1, y1, z1) + add;
// a.setEng(x1, y1, z1, newSrcEng);
// }
}
// if (e < ovb) //溃坝阀值
// continue;
// if (ovf > 0.1) { //溢流阀
// e = e - ovf;
// }
e = e * res; //静态电阻
// e = e * dres * .0001f; //动态电阻0~10000之间
// if (drs > 0.1) { //如果动态电阻敏感系统存在,则每次传送一次能量,电阻就变小一次
// dres += drs;
// if (dres > 10000)
// dres = 10000;
// }
if (not > 0.5) //反相器
e = -e;
// if (mul > 0.1) {
// e = e * 10 * mul; //mul是乘法器mul在0~1之间但是它控制的倍率在0~10倍之间
// }
a.addEng(x2, y2, z2, e); //能量传到target cell
}
}
//TODO 1 模式识别的环境模拟和判定 2.静态参数的初值、变异 ,上面这个方法只是随便写的,很可能跑不出结果,要调整为大多数参数不起作为,即大多数情况下设为0
public static void drawOnBrainPicture(Animal a, BrainPicture pic) {// 画出所有细胞间连线
for (int[] l : a.lines) {
float f1 = RandomUtils.nextFloat() * .1f;//稍微调整下位置好看出来是否有多条线在同一个位置
float f2 = RandomUtils.nextFloat() * .1f;
pic.drawCentLine(l[1] + f1, l[2] + f2, l[3] + f2, l[4] + f1, l[5], l[6]);
pic.drawTextCenter(0.5f * (l[1] + l[4]), 0.5f * (l[2] + l[5]), 0.5f * (l[3] + l[6]), Integer.toString(l[0]), 0.15f);
pic.drawPointCent(l[4] + f1, l[5], l[6], .1f);
}
}
}