This post is also available in: 日本語 (Japanese)
オライリーの集合知プログラミングの277ページあたり、遺伝的プログラミングのツリー構造のソースコードを読み解いてみました。プログラムにメモ的に書いたので、pythonのコメントアウト(#)がそのままです。
一連のプログラムは以下のとおりで、evaluate([2,3])で1が返り、evaluate([5,3])で8が返ってきます。
Contents
遺伝的プログラミングのツリー構造のプログラムの部分
#!/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に変えて処理する。



