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に変えて処理する。