サイトアイコン Amelt.net

集合知プログラミング:遺伝的プログラミングのツリー構造のソースコードを読み解く

This post is also available in: English-US (英語)

オライリーの集合知プログラミングの277ページあたり、遺伝的プログラミングのツリー構造のソースコードを読み解いてみました。プログラムにメモ的に書いたので、pythonのコメントアウト(#)がそのままです。
一連のプログラムは以下のとおりで、evaluate([2,3])で1が返り、evaluate([5,3])で8が返ってきます。

遺伝的プログラミングのツリー構造のプログラムの部分

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import codecs

sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
# ここまで、pythonで日本語を使うためのおまじない


from random import random,randint,choice
from copy import deepcopy
from math import log

class fwrapper:
  def __init__(self,function,childcount,name):
    self.function=function
    self.childcount=childcount
    self.name=name

class node:
  def __init__(self,fw,children):
    self.function=fw.function
    self.name=fw.name
    self.children=children

  def evaluate(self,inp):
    results=[n.evaluate(inp) for n in self.children]
    return self.function(results)

class paramnode:
  def __init__(self,idx):
    self.idx=idx

  def evaluate(self,inp):
    return inp[self.idx]

class constnode:
  def __init__(self,v):
    self.v=v
  def evaluate(self,inp):
    return self.v

addw=fwrapper(lambda l:l[0]+l[1],2,'add')
subw=fwrapper(lambda l:l[0]-l[1],2,'subtract')
mulw=fwrapper(lambda l:l[0]*l[1],2,'multiply')

def iffunc(l):
  if l[0]>0: return l[1]
  else: return l[2]
ifw=fwrapper(iffunc,3,'if')

def isgreater(l):
  if l[0]>l[1]: return 1
  else: return 0
gtw=fwrapper(isgreater,2,'isgreater')

flist=[addw,mulw,ifw,gtw,subw]

def exampletree():
  return node(ifw,[
                  node(gtw,[paramnode(0),constnode(3)]),
                  node(addw,[paramnode(1),constnode(5)]),
                  node(subw,[paramnode(1),constnode(2)]),
                  ]
              )

# 集合知プログラミング本(P277)ではインタプリタで実行している部分
exampletree = exampletree()
# evaluate([2,3])で、1が返ってくる
print exampletree.evaluate([2,3])
# evaluate([5,3])で、8が返ってくる
print exampletree.evaluate([5,3])

print exampletree.evaluate([2,3])は以下のように書き換えられる

# ここから読み解いていった内容
# print exampletree.evaluate([2,3])は以下のように書き換えられる。

print node(ifw,[
                  node(gtw,[paramnode(0),constnode(3)]),
                  node(addw,[paramnode(1),constnode(5)]),
                  node(subw,[paramnode(1),constnode(2)]),
                  ]
              ).evaluate([2,3])

nodeクラスで処理される

# def __init__(self,fw,children)の引数fw,childrenはそれぞれ

# (ifw,[
#     node(gtw,[paramnode(0),constnode(3)]),
#     node(addw,[paramnode(1),constnode(5)]),
#     node(subw,[paramnode(1),constnode(2)]),
#   ])

# に対応している。
# nodeクラス中のdef evaluate(self,inp)には、inp にリスト[2,3]が入る

nodeクラス中のself.function

# self.function=fw.function。ifw=fwrapper(iffunc,3,'if')なので、この時のself.functionはdef iffunc(l):
# そして、def evaluate(self,inp)のなかの、return self.function(results)によって返ってくる
# self.children の中身は 

#   [
#     node(gtw,[paramnode(0),constnode(3)]),
#     node(addw,[paramnode(1),constnode(5)]),
#     node(subw,[paramnode(1),constnode(2)]),
#   ]

resultsの中の内容

# なので、resultsの中は以下の内容が入ってる(と、思う)。そしてresultsはdef iffunc(l)の引数となる。

#     [
#      node(gtw,[paramnode(0),constnode(3)]).evaluate([2,3]),
#      node(addw,[paramnode(1),constnode(5)]).evaluate([2,3]),
#      node(subw,[paramnode(1),constnode(2)]).evaluate([2,3]),
#     ]

return self.function(results)の返り値

# その結果、return self.function(results)の返り値、すなわちiffuncの挙動は以下のような感じになる。

# def iffunc(results):
#   if node(gtw,[paramnode(0),constnode(3)]).evaluate([2,3]) > 0: return node(addw,[paramnode(1),constnode(5)]).evaluate([2,3])
#   else:node(subw,[paramnode(1),constnode(2)]).evaluate([2,3])

もう一回、nodeクラスで処理される

# で、if node(gtw,[paramnode(0),constnode(3)]).evaluate([2,3]) >0 
# のnode(gtw,[paramnode(0),constnode(3)]).evaluate([2,3])の部分がまたnodeクラスの引数となる。

self.children の中の内容

# self.children の中身は,
# [paramnode(0),constnode(3)]
# なので、resultsの中は以下の内容が入ってる(と思う)。

# [
# paramnode(0).evaluate([2,3],
# constnode(3).evaluate([2,3]
# ]

gtwの時のself.function

# self.function=fw.function。gtw=fwrapper(isgreater,2,'isgrater')なので、この時のself.functionはdef isgrater(l):
# そしてresultsはdef isgreater(l)の引数となる。def isgreater(l)はこんな感じになる。

#  def isgreater(l)
#   if paramnode(0).evaluate([2,3]) > constnode(3).evaluate([2,3]): return 1
# else: retuen 0

paramnode、constnodeクラスで処理

# で、paramnode、constnodeクラスで処理される。inpには[2,3]が引数として入っている。

# paramnodeクラスは2を返す
# self.idx = idx = 0
# inp[self.idx] = inp[0] = 2

# constnodeクラスは3を返す
# self.v = v = 3

def isgreater(l)の返り値

# よって、def isgreater(l)の返り値は、return 0
# ここで、def iffunc(results)の返り値は、else:node(subw,[paramnode(1),constnode(2)]).evaluate([2,3])
# あとは、gtwがsubwに変わっただけなので同じように処理される。

# subw=fwrapper(lambda l:l[0]-l[1],2,'subtract')
# paramnodeクラス
# self.idx = idx = 1
# inp[self.idx] = inp[1] = 3

# constnodeクラス
# self.v = v = 2

# 従って、
# l[0] = 3
# l[1] = 2
# よって、exampletree.evaluate([2,3])の返り値は1

evaluate([5,3])の場合

# もし、evaluate([5,3])の場合なら...
# で、paramnode、constnodeクラスで処理される。inpには[5,3]が引数として入っている。

# paramnodeクラスは2を返す
# self.idx = idx = 0
# inp[self.idx] = inp[0] = 5

# constnodeクラスは3を返す
# self.v = v = 3

# よって、def iffunc(results)の返り値は、return node(addw,[paramnode(1),constnode(5)]).evaluate([2,3])
# あとは、evaluate([2,3])の場合のsubwを、addwに変えて処理する。