From 8998e2eb104c0965b0c9f190705b70abc16b62d7 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Thu, 29 Feb 2024 02:29:14 +0100 Subject: Removed information EAT-ing. [#16] Allowing organisms to EAT information is causing huge reefs to form too early and occupy most memory space. Here we replace EATB/F with the old shift left/right. This is done to compare simulation stability. --- README.md | 21 +---- include/evolver.h | 3 +- include/instset.h | 6 +- include/process.h | 5 +- src/process.c | 253 ++++++++++++++++-------------------------------------- 5 files changed, 85 insertions(+), 203 deletions(-) diff --git a/README.md b/README.md index d3c1cd6..df9b417 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ the addition of a seeker pointer to all organisms. The seeker pointer (SP) is an attempt to bring extra spatial and temporal coherence to the simulation. Allocation, reads and writes will take more time when done between addresses that are far away, as a consequence of the SP -having to travel those distances at a constant speed. +having to travel those distances at a speed of 1 byte per simulation cycle +(SALIS' speed-of-light if you will). To watch an introductory video about SALIS (v1.0) [go here.](https://www.youtube.com/watch?v=jCFmOCvy6po) @@ -30,8 +31,6 @@ the following elements: - One or two associated memory blocks - One instruction pointer - One seeker pointer -- One reward pointer -- One punishment pointer - 4 general-purpose registers - A stack of 8 values @@ -49,8 +48,6 @@ mutation scheme is enough to allow evolution by natural selection to occur. SALIS' organisms read a simple language similar to ASM. This language consists of 32 instructions, each with an associated name and symbol. Whenever an organism performs an invalid instruction it is considered a *fault*. -When a *fault* is committed by any organism it gets punished, and stops its -execution during a full simulation cycle. ### Faults may be caused by: - Not having enough register modifiers located after the current instruction @@ -60,13 +57,6 @@ execution during a full simulation cycle. - SP being on address non-adjacent to child memory block, while allocating - Swapping or splitting when not owning 2 memory blocks - Dividing by zero -- Attempting to eat from an allocated (but not owned) or invalid address - -### Entropy -SALIS-2.0 introduces the EAT instruction and the concept of entropy. -Organisms may now 'consume' information (in the form of instructions) and trade -them for extra execution speed. When eaten, information gets destroyed -(randomized). ### The Common Pipe The common pipe is a mechanism through which different SALIS simulations can @@ -93,6 +83,8 @@ to/from this common pipe via the SEND/RCVE instructions. |`SPLT` |`$` |0 |Split child memory block | |`INCN` |`^` |1 |Increment register | |`DECN` |`v` |1 |Decrement register | +|`SHFL` |`<` |1 |Shift-left register | +|`SHFR` |`>` |1 |Shift-right register | |`ZERO` |`0` |1 |Zero out register | |`UNIT` |`1` |1 |Place 1 on register | |`NOTN` |`!` |1 |Negation operator | @@ -107,8 +99,6 @@ to/from this common pipe via the SEND/RCVE instructions. |`RECV` |`R` |1 |Receive instruction from common pipe | |`PSHN` |`#` |1 |Push value to stack | |`POPN` |`~` |1 |Pop value from stack | -|`EATB` |`<` |0 |Eat backwards | -|`EATF` |`>` |0 |Eat forward | Instructions with arguments *must* be followed by a minimum number of register modifiers equal to the instruction's parameters. Otherwise the instruction will @@ -147,9 +137,6 @@ Look at README file inside the `./bin` directory for a full list of commands. - Tierran templates are now used instead of keys/lock pairs - The instruction set is shorter - Organisms can send/receive instructions to/from a common pipe -- Organisms can "eat" information -- Organisms are rewarded for eating -- Organisms are punished on faults ### Python integration - Salis controller/viewer is now written in python 3 diff --git a/include/evolver.h b/include/evolver.h index b2ead10..457cd9a 100644 --- a/include/evolver.h +++ b/include/evolver.h @@ -4,8 +4,7 @@ * * This module controls all random events in Salis. At its heart lies a * XOR-Shift pseudo-random number generator with 128 bits of state. It controls -* cosmic rays and rises simulation entropy whenever organisms 'eat' -* information. +* cosmic rays and slowly rises simulation entropy. */ #ifndef SALIS_EVOLVER_H diff --git a/include/instset.h b/include/instset.h index ab7bab0..eeb4600 100644 --- a/include/instset.h +++ b/include/instset.h @@ -32,6 +32,8 @@ enum { SALIS_INST SPLT, /**< $ Split child memory block */ SALIS_INST INCN, /**< ^ Increment register */ SALIS_INST DECN, /**< v Decrement register */ + SALIS_INST SHFL, /**< < Shift-left register */ + SALIS_INST SHFR, /**< > Shift-right register */ SALIS_INST ZERO, /**< 0 Zero out register */ SALIS_INST UNIT, /**< 1 Place 1 on register */ SALIS_INST NOTN, /**< ! Negation operator */ @@ -45,9 +47,7 @@ enum { SALIS_INST SEND, /**< S Send instruction to common pipe */ SALIS_INST RECV, /**< R Receive instruction from common pipe */ SALIS_INST PSHN, /**< # Push value to stack */ - SALIS_INST POPN, /**< ~ Pop value from stack */ - SALIS_INST EATB, /**< < Eat backwards */ - SALIS_INST EATF /**< > Eat forward */ + SALIS_INST POPN /**< ~ Pop value from stack */ }; /** Determine if an unsigned integer contains a valid instruction. diff --git a/include/process.h b/include/process.h index 3723ad6..bd46462 100644 --- a/include/process.h +++ b/include/process.h @@ -5,8 +5,7 @@ * This module allows access to Salis processes, or procs. Procs are the actual * organisms in the simulation. They consist of a virtual CPU with 4 registers * and a stack of 8. The instruction pointer (IP) and seeker pointer (SP) -* coordinate the execution of all instructions. Organisms get rewarded or -* punished, depending on certain conditions. +* coordinate the execution of all instructions. */ #ifndef SALIS_PROCESS_H @@ -21,8 +20,6 @@ struct Process SALIS_PROC_ELEMENT uint32 mb1s; SALIS_PROC_ELEMENT uint32 mb2a; SALIS_PROC_ELEMENT uint32 mb2s; - SALIS_PROC_ELEMENT uint32 reward; - SALIS_PROC_ELEMENT uint32 punish; SALIS_PROC_ELEMENT uint32 ip; SALIS_PROC_ELEMENT uint32 sp; SALIS_PROC_ELEMENT uint32 rax; diff --git a/src/process.c b/src/process.c index 4004ae1..7483aa7 100644 --- a/src/process.c +++ b/src/process.c @@ -510,13 +510,12 @@ static void toggle_ip_flag(void (*toggler)(uint32 address)) static void on_fault(uint32 pidx) { - /* Organisms get punished whenever they execute an invalid instruction - (commit a 'fault') by having the halt one simulation cycle. + /* For now, faults do nothing. */ assert(g_is_init); assert(pidx < g_capacity); assert(!sal_proc_is_free(pidx)); - g_procs[pidx].punish = 1; + (void)pidx; } static void increment_ip(uint32 pidx) @@ -995,6 +994,12 @@ static void one_reg_op(uint32 pidx, uint8 inst) case DECN: (*reg)--; break; + case SHFL: + (*reg) <<= 1; + break; + case SHFR: + (*reg) >>= 1; + break; case ZERO: (*reg) = 0; break; @@ -1266,188 +1271,82 @@ pop(uint32 pidx) increment_ip(pidx); } -static boolean eat_seek(uint32 pidx, boolean forward) -{ - /* Search (via the seeker pointer) for an identical copy of the memory - stream right in front of the calling organism's IP. This function gets - called by organisms each cycle during an EAT instruction. Only when a valid - copy is found, this function will return TRUE. */ - uint32 next_addr; - uint8 next_inst; - uint8 sp_inst; - assert(g_is_init); - assert(pidx < g_capacity); - assert(!sal_proc_is_free(pidx)); - next_addr = g_procs[pidx].ip + 1; - - if (!sal_mem_is_address_valid(next_addr)) { - on_fault(pidx); - increment_ip(pidx); - return FALSE; - } - - /* Processes may only eat code copies from memory areas that are either - deallocated or owned by them (i.e. writeable). - */ - if ( - !is_writeable_by(pidx, g_procs[pidx].sp) || - g_procs[pidx].sp == next_addr - ) { - increment_sp(pidx, forward); - return FALSE; - } - - next_inst = sal_mem_get_inst(next_addr); - sp_inst = sal_mem_get_inst(g_procs[pidx].sp); - - if (next_inst == sp_inst) { - return TRUE; - } - - increment_sp(pidx, forward); - return FALSE; -} - -static void eat(uint32 pidx) -{ - /* Salisian organisms may 'eat' information. They eat by searching for - 'copies' of the code in front of their IPs during the EAT instruction. When - a valid copy is found, an organism gets rewarded by setting their 'reward' - field to the length of the measured copy. Each cycle, organisms execute - 'reward' number of instructions plus one, thus, eating a larger stream - produces a larger advantage for an organism. - - However, whenever an organism eats, the detected copy of the source code - gets destroyed (randomized). The main idea of the EAT instruction is to - turn 'information' into a valuable resource in Salis. Organisms, - nonetheless, may only eat information which they have 'write' access to. - */ - uint32 source; - uint32 target; - assert(g_is_init); - assert(pidx < g_capacity); - assert(!sal_proc_is_free(pidx)); - source = g_procs[pidx].ip + 1; - target = g_procs[pidx].sp; - assert(sal_mem_is_address_valid(source)); - assert(sal_mem_get_inst(source) == sal_mem_get_inst(target)); - g_procs[pidx].reward = 0; - - while ( - sal_mem_is_address_valid(source) && - sal_mem_is_address_valid(target) && - sal_mem_get_inst(source) == sal_mem_get_inst(target) - ) { - g_procs[pidx].reward++; - _sal_evo_randomize_at(target); - source++; - target++; - } - - increment_ip(pidx); -} - static void proc_cycle(uint32 pidx) { - /* Cycle a process once. During each process cycle, several things may - happen. For example, if a process is being punished (for committing a - fault), it will have to wait until the next simulation cycle to be able to - execute. - - Non-punished organisms execute at least one instruction per simulation - cycle. If they are being rewarded, they execute one, plus the number on - their 'reward' field, number of instructions each cycle. + /* Cycle a process once. Organisms will always execute one instruction per + simulation cycle. */ - uint32 cycles; + uint8 inst; assert(g_is_init); assert(pidx < g_capacity); assert(!sal_proc_is_free(pidx)); - /* Organism is being punished. Clear its 'punish' field and return without - executing. - */ - if (g_procs[pidx].punish) { - g_procs[pidx].punish = 0; - return; - } + inst = sal_mem_get_inst(g_procs[pidx].ip); + g_instructions_executed++; - /* Execute one instruction per number of 'reward' points awarded to this - organism. Switch case associates each instruction to its corresponding - instruction handler. Process module keeps track of the total number of - instructions executed (by all organisms) per simulation cycle. - */ - for (cycles = 0; cycles < g_procs[pidx].reward + 1; cycles++) { - uint8 inst = sal_mem_get_inst(g_procs[pidx].ip); - g_instructions_executed++; - - switch (inst) { - case JMPB: - if (jump_seek(pidx, FALSE)) jump(pidx); - break; - case JMPF: - if (jump_seek(pidx, TRUE)) jump(pidx); - break; - case ADRB: - if (addr_seek(pidx, FALSE)) addr(pidx); - break; - case ADRF: - if (addr_seek(pidx, TRUE)) addr(pidx); - break; - case MALB: - alloc(pidx, FALSE); - break; - case MALF: - alloc(pidx, TRUE); - break; - case SWAP: - swap(pidx); - break; - case SPLT: - split(pidx); - break; - case INCN: - case DECN: - case ZERO: - case UNIT: - case NOTN: - one_reg_op(pidx, inst); - break; - case IFNZ: - if_not_zero(pidx); - break; - case SUMN: - case SUBN: - case MULN: - case DIVN: - three_reg_op(pidx, inst); - break; - case LOAD: - load(pidx); - break; - case WRTE: - write(pidx); - break; - case SEND: - send(pidx); - break; - case RECV: - receive(pidx); - break; - case PSHN: - push(pidx); - break; - case POPN: - pop(pidx); - break; - case EATB: - if (eat_seek(pidx, FALSE)) eat(pidx); - break; - case EATF: - if (eat_seek(pidx, TRUE)) eat(pidx); - break; - default: - increment_ip(pidx); - } + switch (inst) { + case JMPB: + if (jump_seek(pidx, FALSE)) jump(pidx); + break; + case JMPF: + if (jump_seek(pidx, TRUE)) jump(pidx); + break; + case ADRB: + if (addr_seek(pidx, FALSE)) addr(pidx); + break; + case ADRF: + if (addr_seek(pidx, TRUE)) addr(pidx); + break; + case MALB: + alloc(pidx, FALSE); + break; + case MALF: + alloc(pidx, TRUE); + break; + case SWAP: + swap(pidx); + break; + case SPLT: + split(pidx); + break; + case INCN: + case DECN: + case SHFL: + case SHFR: + case ZERO: + case UNIT: + case NOTN: + one_reg_op(pidx, inst); + break; + case IFNZ: + if_not_zero(pidx); + break; + case SUMN: + case SUBN: + case MULN: + case DIVN: + three_reg_op(pidx, inst); + break; + case LOAD: + load(pidx); + break; + case WRTE: + write(pidx); + break; + case SEND: + send(pidx); + break; + case RECV: + receive(pidx); + break; + case PSHN: + push(pidx); + break; + case POPN: + pop(pidx); + break; + default: + increment_ip(pidx); } } -- cgit v1.2.1