2016-10-26 15:15:00 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace neuralnetwork\core;
|
|
|
|
|
|
|
|
use filemanager\core\FileManager;
|
|
|
|
|
2016-10-26 16:07:52 +00:00
|
|
|
class Genome implements \Serializable{
|
2016-10-26 15:15:00 +00:00
|
|
|
|
|
|
|
/************************************************
|
|
|
|
**** Constants ****
|
|
|
|
************************************************/
|
|
|
|
const MIN = 0;
|
|
|
|
const MAX = 1e9;
|
|
|
|
|
|
|
|
/************************************************
|
|
|
|
**** LOCAL ATTRIBUTES ****
|
|
|
|
************************************************/
|
2016-10-26 15:24:51 +00:00
|
|
|
public $layers; // Number of layers
|
|
|
|
public $neurons; // Neurons of the genome
|
|
|
|
public $synapses; // Synapses between neurons
|
2016-10-26 15:15:00 +00:00
|
|
|
|
|
|
|
|
2016-10-26 16:07:52 +00:00
|
|
|
/************************************************
|
|
|
|
**** Genome Construction ****
|
|
|
|
************************************************/
|
|
|
|
|
2016-10-26 15:15:00 +00:00
|
|
|
/* CONSTRUCTOR
|
|
|
|
*
|
|
|
|
* -- RANDOM CREATION --
|
|
|
|
* @layers<int> Number of layers to manage
|
|
|
|
* @neurons<int> Number of neurons per layer
|
|
|
|
*
|
|
|
|
* -- CLONING --
|
|
|
|
* @base<Genom> Genome to clone to
|
|
|
|
*
|
|
|
|
* -- CROSS-OVER CREATION --
|
|
|
|
* @father<Genome> First parent
|
|
|
|
* @mother<Genome> Second parent
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public function __construct(){
|
|
|
|
/* (1) Get arguments */
|
|
|
|
$argv = func_get_args();
|
|
|
|
$argc = count($argv);
|
|
|
|
|
|
|
|
|
|
|
|
/* (2) If CrossoverCreation */
|
|
|
|
if( $argc > 1 && $argv[0] instanceof Genome && $argv[1] instanceof Genome )
|
|
|
|
$this->construct_crossover($argv[0], $argv[1]);
|
|
|
|
|
|
|
|
/* (3) If RandomCreation */
|
|
|
|
else if( $argc > 1 && abs(intval($argv[0])) === $argv[0] && abs(intval($argv[1])) === $argv[1] )
|
|
|
|
$this->construct_new($argv[0], $argv[1]);
|
|
|
|
|
|
|
|
/* (4) If InheritanceCreation (clone) */
|
|
|
|
else if( $argc > 0 && $argv[0] instanceof Genome )
|
|
|
|
$this->construct_inheritance($argv[0]);
|
|
|
|
|
|
|
|
/* (5) If no match */
|
|
|
|
else
|
|
|
|
throw new \Error('Invalid Genome constructor\'s arguments.');
|
|
|
|
}
|
|
|
|
|
|
|
|
/* BUILDS A Genome RANDOMLY WITH PARAMETERS
|
|
|
|
*
|
|
|
|
* @layers<int> The number of hidden layers to manage
|
|
|
|
* @neurons<int> The number neurons per layer
|
|
|
|
*
|
|
|
|
* @return created<Boolean> If Genome has been successfully created
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
private function construct_new($layers=-1, $neurons=-1){
|
|
|
|
/* (1) Checks parameters */
|
|
|
|
if( abs(intval($layers)) !== $layers || abs(intval($neurons)) !== $neurons )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// set layers
|
|
|
|
$this->layers = $layers;
|
|
|
|
|
|
|
|
/* (2) Creating random neurons */
|
|
|
|
$this->neurons = [];
|
2016-10-26 15:24:51 +00:00
|
|
|
for( $i = 0, $l = $neurons*$layers ; $i < $l ; $i++ )
|
2016-10-26 15:15:00 +00:00
|
|
|
$this->neurons[$i] = rand(self::MIN, self::MAX) / self::MAX;
|
|
|
|
|
|
|
|
/* (3) Creating random synapses */
|
|
|
|
$this->synapses = [];
|
|
|
|
for( $i = 0, $l = pow($neurons, $layers) ; $i < $l ; $i++ )
|
|
|
|
$this->synapses[$i] = rand(self::MIN, self::MAX) / self::MAX;
|
|
|
|
|
|
|
|
// Success status
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* BUILDS A Genome BASED ON A PARENT
|
|
|
|
*
|
|
|
|
* @parent<Genome> Parent genome to clone into children
|
|
|
|
*
|
|
|
|
* @return created<Boolean> If cloned Genome has been created successfully
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
private function construct_inheritance($parent=null){
|
|
|
|
/* (1) Checks parent type */
|
|
|
|
if( !($parent instanceof Genome) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* (2) Clones into this Genome */
|
|
|
|
$this->layers = $parent->layers;
|
2016-10-26 15:24:51 +00:00
|
|
|
$this->neurons = array_slice($parent->neurons, 0);
|
|
|
|
$this->synapses = array_slice($parent->synapses, 0);
|
2016-10-26 15:15:00 +00:00
|
|
|
|
|
|
|
// Success state
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* BUILDS A Genome BASED ON TWO PARENTS
|
|
|
|
*
|
|
|
|
* @father<Genome> First parent ($father)
|
|
|
|
* @mother<Genome> Second parent ($mother)
|
|
|
|
*
|
|
|
|
* @return created<Boolean> If crossed-over Genome has been created successfully
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
private function construct_crossover($father=null, $mother=null){
|
|
|
|
/* (1) Checks parent type */
|
|
|
|
if( !($father instanceof Genome) || !($mother instanceof Genome) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* (2) Checks number of layers+neurons (same species) */
|
|
|
|
if( $father->layers !== $mother->layers || count($father->neurons) !== count($mother->neurons) )
|
|
|
|
return false;
|
|
|
|
|
2016-10-26 15:24:51 +00:00
|
|
|
/* (3) Set layer count */
|
|
|
|
$this->layers = $father->layers;
|
|
|
|
|
|
|
|
/* (4) Do random crossover for neurons */
|
2016-10-26 15:15:00 +00:00
|
|
|
$this->neurons = [];
|
|
|
|
for( $i = 0, $l = count($father->neurons) ; $i < $l ; $i++ )
|
2016-10-26 15:24:51 +00:00
|
|
|
if( !!rand(0,1) ) $this->neurons[$i] = $father->neurons[$i];
|
|
|
|
else $this->neurons[$i] = $mother->neurons[$i];
|
2016-10-26 15:15:00 +00:00
|
|
|
|
2016-10-26 15:24:51 +00:00
|
|
|
/* (5) Do random crossover for synapses */
|
2016-10-26 15:15:00 +00:00
|
|
|
$this->synapses = [];
|
2016-10-26 15:24:51 +00:00
|
|
|
for( $i = 0, $l = pow(count($this->neurons)/$this->layers, $this->layers) ; $i < $l ; $i++ )
|
|
|
|
if( !!rand(0,1) ) $this->synapses[$i] = $father->synapses[$i];
|
|
|
|
else $this->synapses[$i] = $mother->synapses[$i];
|
2016-10-26 15:15:00 +00:00
|
|
|
|
|
|
|
// Success state
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-10-26 15:53:56 +00:00
|
|
|
|
2016-10-26 16:07:52 +00:00
|
|
|
/************************************************
|
|
|
|
**** Genome Actions ****
|
|
|
|
************************************************/
|
|
|
|
|
2016-10-26 15:53:56 +00:00
|
|
|
/* APPLIES A MUTATION ON THE Genome WITH A SPECIFIC @threshold
|
|
|
|
*
|
|
|
|
* @threshold<double> Mutation threshold
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public function mutation($threshold=0.5){
|
|
|
|
/* (1) Checks @threshold argument */
|
|
|
|
if( floatval($threshold) !== $threshold || $threshold < 0 || $threshold > 1 )
|
|
|
|
throw new \Error('Invalid threshold for Genome mutation.');
|
|
|
|
|
|
|
|
/* (2) Calculates how many neurons/synapses to mutate */
|
|
|
|
$neuronsMutations = round( (count($this->neurons) - 1) * $threshold );
|
|
|
|
$synapsesMutations = round( (count($this->synapses) - 1) * $threshold );
|
|
|
|
|
|
|
|
/* (3) Choose random neurons' indexes */
|
|
|
|
$iNeurons = [];
|
|
|
|
while( count($iNeurons) < $neuronsMutations ){
|
|
|
|
$r = rand(0, count($this->neurons)-1);
|
|
|
|
|
|
|
|
if( !in_array($r, $iNeurons) )
|
|
|
|
$iNeurons[] = $r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (4) Choose random synapses' indexes */
|
|
|
|
$iSynapses = [];
|
|
|
|
while( count($iSynapses) < $synapsesMutations ){
|
|
|
|
$r = rand(0, count($this->synapses)-1);
|
|
|
|
|
|
|
|
if( !in_array($r, $iSynapses) )
|
|
|
|
$iSynapses[] = $r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (5) Update chosen neurons */
|
|
|
|
for( $i = 0, $l = count($iNeurons) ; $i < $l ; $i++ )
|
|
|
|
$this->neurons[$iNeurons[$i]] = rand(self::MIN, self::MAX) / self::MAX;
|
|
|
|
|
|
|
|
/* (6) Update chosen synapses */
|
|
|
|
for( $i = 0, $l = count($iSynapses) ; $i < $l ; $i++ )
|
|
|
|
$this->synapses[$iSynapses[$i]] = rand(self::MIN, self::MAX) / self::MAX;
|
|
|
|
}
|
2016-10-26 16:07:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
/************************************************
|
|
|
|
**** Serialization ****
|
|
|
|
************************************************/
|
|
|
|
|
|
|
|
/* SERIALIZES A Genome
|
|
|
|
*
|
|
|
|
* @return serialized<String> Serialized representation of the Genome
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public function serialize(){
|
|
|
|
/* (1) Initialize result */
|
|
|
|
$csv = '';
|
|
|
|
|
|
|
|
/* (2) Adds global attributes */
|
|
|
|
$csv .= $this->layers .';';
|
|
|
|
|
|
|
|
/* (3) Adds neurons data */
|
|
|
|
$csv .= implode(',', $this->neurons) .';';
|
|
|
|
|
|
|
|
/* (4) Adds synapses data */
|
|
|
|
$csv .= implode(',', $this->synapses);
|
|
|
|
|
|
|
|
return $csv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* BUILDS A Genome BASED ON HIS SERIALIZED REPRESENTATION
|
|
|
|
*
|
|
|
|
* @serialized<String> Serialized representation of a Genome
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public function unserialize($serialized){
|
|
|
|
/* (1) Segmenting data */
|
|
|
|
$segments = explode(';', $serialized);
|
|
|
|
|
|
|
|
// Manage segmentation error
|
|
|
|
if( count($segments) < 3 )
|
|
|
|
throw new \Error('Format error during Genome unserialization.');
|
|
|
|
|
|
|
|
/* (2) Get global attributes */
|
|
|
|
if( !is_numeric($segments[0]) )
|
|
|
|
throw new \Error('Format error during Genome unserialization.');
|
|
|
|
|
|
|
|
$this->layers = intval($segments[0]);
|
|
|
|
|
|
|
|
/* (3) Get neurons values */
|
|
|
|
$this->neurons = explode(',', $segments[1]);
|
|
|
|
|
|
|
|
/* (4) Get synapses values */
|
|
|
|
$this->synapses = explode(',', $segments[2]);
|
|
|
|
}
|
|
|
|
|
2016-10-26 15:15:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/************************************************
|
|
|
|
**** USE CASE ****
|
|
|
|
************************************************/
|
|
|
|
$use_case = false;
|
|
|
|
if( $use_case ){
|
|
|
|
|
|
|
|
/* (1) Basic Creation */
|
|
|
|
$a = new Genome(2, 3); // 2 layers of 3 neurons each -> randomly filled
|
|
|
|
|
|
|
|
/* (2) Inheritance */
|
|
|
|
$b = new Genome($a); // Clone of @a
|
|
|
|
|
|
|
|
/* (3) Section Title */
|
|
|
|
$b->mutation(0.3); // @b has now mutated with a threshold of 30%
|
|
|
|
|
|
|
|
/* (4) Cross-over (father+mother) */
|
|
|
|
$c = new Genome($a, $b); // @c is a randomly-done mix of @a and @b
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
?>
|