1.6 RubyNEAT DSL

I will take the '''XOR''' neater and document it. This is not the perfect way to go, but I will get more extensive later.

1.6.1 The XOR Neater Example

require 'xor'
include NEAT::DSL
  • The first lines here includes the special XOR library, which is basically:

def xor(*inp)
inp.map{|n| (n > 0) ? 1 : 0}.reduce {|p, i| p + ((i > 0) ? 1 : 0) } == 1

-Basic settings for the '''XOR''', which can handle more than 2 inputs.

  • The actual definition of the Neater. Here you specify the parameters RubyNEAT will use to run the evolution, as well as the CPPN neuron types, the fitness function, etc.

define "XOR System" do
  • Inputs defined as name: Neuron, name: Neuron … hash. In this segment, we create a block to generate the hash since we can have a variable number of inputs to the XOR. The input names must be unique. Note that a bias neuron is also supplied, and it is always called :bias.

inputs {
cinv = Hash[(1..XOR_INPUTS).map{|i| [("i%s" % i).to_sym, InputNeuron]}]
cinv[:bias] = BiasNeuron
  • Outputs are defined in a similar fashion to the inputs. The names of all the output neurons must be unique. Here in this example we only have one output, and we use the hyperbolic tan Neuron as the output. There is also a sigmoid Neuron that could be used as well, but the input levels would have to be conditioned to vary from 0 to 1 instead of from -1 to one.

outputs out: TanhNeuron
  • Hidden neuron specification is optional. The names given here are largely meaningless, but but follow the same rules for uniqueness. The neurons specified will be selected randomly as the topologies are augmented.

hidden tan: TanhNeuron
  1. Settings

    For RubyNEAT. Extensive documentation will be provided on a later date as to the meanings, which closely follow the parameters for Ken Stanley's NEAT implementation.

    1. General

      hash_on_fitness false
      start_population_size 30
      population_size 30
      max_generations 10000
      max_population_history 10
    2. Evolver probabilities and SDs


      mutate_perturb_gene_weights_prob 0.10
      mutate_perturb_gene_weights_sd 0.25
    3. Complete Change of weight

      mutate_change_gene_weights_prob 0.10
      mutate_change_gene_weights_sd 1.00
    4. Adding new neurons and genes

      mutate_add_neuron_prob 0.05
      mutate_add_gene_prob 0.20
    5. Switching genes on and off

      mutate_gene_disable_prob 0.01
      mutate_gene_reenable_prob 0.01
      interspecies_mate_rate 0.03
      mate_only_prob 0.10 *0.7
    6. Mating

      survival_threshold 0.20 # top % allowed to mate in a species.
      survival_mininum_per_species 4 # for small populations, we need SOMETHING to go on.
    7. Fitness costs

      fitness_cost_per_neuron 0.00001
      fitness_cost_per_gene 0.00001
    8. Speciation

      compatibility_threshold 2.5
      disjoint_coefficient 0.6
      excess_coefficient 0.6
      weight_coefficient 0.2
      max_species 20
      dropoff_age 15
      smallest_species 5
    9. Sequencing

      The evaluation function is called repeatedly, and each iteration is given a monotonically increasing integer which represents the sequence number. The results of each run is returned, and those results are evaluated elsewhere in the Neater.

      start_sequence_at 0
      end_sequence_at 2 ** XOR_INPUTS - 1
  2. The Evolution Block

    evolve do
    1. The Query Block

      This query shall return a vector result that will serve as the inputs to the critter.

      query { |seq|
      * We'll use the seq to create the xor sequences via
      * the least signficant bits.
      condition_boolean_vector (0 ... XOR_INPUTS).map{|i| (seq & (1 << i)) != 0}
    2. The Compare Block

      Compare the fitness of two critters. We may choose a different ordering here.

      compare {|f1, f2| f2 <=> f1 }
    3. The Cost of Fitness Block

      Here we integrate the cost with the fitness.

      cost { |fitvec, cost|
      fit = XOR_STATES - fitvec.reduce {|a,r| a+r} - cost
      $log.debug ">>>>>>> fitvec *{fitvec} => *{fit}, cost *{cost}"
    4. The Fitness Block

      The fitness block is called for each activation and is given the input vector, the output vector, and the sequence number given to the query. The results are evaluated and a fitness scalar is returned.

      +BEGINSRC ruby fitness { |vin, vout, seq| unless vout * :error bin = unconditionbooleanvector vin bout = unconditionbooleanvector vout bactual = [xor(vin)] vactual = conditionbooleanvector bactual fit = (bout ** bactual) ? 0.00 : 1.00 simplefitnesserror(vout, vactual.map{|f| f 0.50 }) bfit = (bout * bactual) ? 'T' : 'F' fit else $log.debug "Error on {vin} [{seq}]" 1.0 end } #+ ENDSRC

    5. The Termination Condition

      When the desired fitness level is reached, you may want to end the Neater run. If so, provide a block to do just that.

      stop_on_fitness { |fitness, c|
      puts "*** Generation Run *{c.generation_num}, best is *{fitness[:best]} ***\n\n"
      fitness[:best] >= ALMOST_FIT
  3. Report Generating Block

    This particular report block just adds something to the log. You could easily replace that with a visual update if you like, etc.

    report do |rept|
    $log.info "REPORT *{rept.to_yaml}"
  4. Engine Run Block

    The block here is called upon the completion of each generation. The 'c' parameter is the RubyNEAT Controller, the same as given to the stoponfitness block.

    run_engine do |c|
    $log.info "******** Run of generation %s completed, history count %d ********" %
    [c.generation_num, c.population_history.size]

1.6.2 Releases

  1. v0.4.0.alpha.4

    • First crude cut of a dashboard rubyneatdashboard

  2. 0.3.5.alpha.6

    • Command line workflow is a bit cleaner

    • Removed neater examples completely and place them in https://github.com/flajann2/rubyneat_examples

    • Cleaned up the internal docs a bit

    • Uniquely Generated Named Objects (UGNOs) cleaned up to be respectable

  3. 2015-06-08

    • Working on the Iterated ES HyperNEAT still, after being side-tracked by having to make a living. Also creating a maze environment for the critters to operate as bots in order to test the new ES HyperNEAT extension.

    • rnDSL, as a result of TWEANN Compositions, is undergoing radical changes. All example Neaters will be eventually update to reflect the new syntax.

  4. 2014-09-25

    Hot on the efforts on adding two major features to RubyNEAT:

    • TWEANN Compositions – you will be able to define composites of TWEANNs on a per critter basis. This should mirror how, say, biological brains composite themselves into regions of speciality. You may specify different selections of neurons for each TWEANN. This is totally experiential, so we'll see if this results in better convergence for some problems.

    • iterated ES HyperNEAT – one of the compsitions above can be specified as a Hyper TWEANN, and just represent one of the many compositions you may have.

    • The syntax of the Neater DSL will change quite a bit to reflect the new features, and all of the examples will be rewritten to show this.

    Do not confuse the ANN compositions here with CPPNs, which are completely different. By default, all TWEANNs in HyperNEAT are potential CPPNs anyway, as you can specify more than one neuron type.

  5. 2014-08-03

    Just released a very crude alpha cut of a dashboard for RubyNEAT. You will have to install it manually, along with rubyneat. The gem is rubyneatdashboard.

    • I am currently working on a Dashboard for RubyNEAT. It will be a gemmable plugin that will allow you to use the browser as the dashboard. It will have realtime updates and the like, allowing you to monitor the progress of your Neaters, and to view and possibly set parameters, and to see what your Critters look like.

Author: Lord Alveric

Created: 2016-03-26 Sat 01:43