Genetic Algorithm on Pharo: Refraction problem

O site do livro Agile Artificial Intelligence se propõe a promoção do ambiente do Pharo Smalltalk, com suas características de interação vívida com o desenvolvedor, usando um assunto moderno que é a Inteligência Artificial. Há muito pensava experimentar no Pharo com Algoritmos Genéticos, um dos assuntos abordados no livro. Aproveitei e fui entrando os códigos no site Genetic Algorithm ao poucos para entendê-lo melhor. Mas você pode evitar a digitação obtendo os fontes em https://github.com/AgileArtificialIntelligence/PharoSourceCode.

Resolvi experimentar criando um exemplo para calcular a trajetória de um raio de luz em três meios com índices de refração diferentes. Os índices serão representados pela velocidade da luz em cada meio.

A figura abaixo mostra a configuração adotada para ilustrar o problema. As três camadas devem ser atravessadas por um raio de luz que em cada camada terá um índice de refração diferente.

Realçamos três partes do loop no método run que vai evoluindo as populações até atingir uma estabilidade que representa uma solução para o problema. O terceiro realce mostra o trecho em que os logs são alimentados com dados sobre as etapas por qual passa a computação. Para mais detalhes acompanhe o capítulo Genetic Algorithm. Estes logs serão usados para criar visualizações e relatórios. Cada entrada neste conjunto de logs tem informação sobre o indivíduo mais apto, o menos apto e a média do desempenho dos indivíduos.

O nosso problema é encontrar os três segmentos que representam a trajetória da luz no três meios diferentes. Os genes para o nosso problema são as duas distâncias em relação ao eixo vertical esquerdo dos pontos onde o raio intercepta as superfícies.

O método GAEngine class>>exampleRefraction abaixo, baseado no exemplo no capítulo Genetic Algorithm, tem suas partes específicas realçadas no interior dos retângulos vermelhos. O primeiro mostra a criação de uma objeto que servirá para fazer os cálculos referentes ao problema da refração. O segundo mostra este objeto calculando o desempenho para determinados genes dada a configuração adotada. O terceiro retângulo define um relatório que vai aparecer numa aba do inspector (mostraremos mais adiante). Acrescentei os métodos GAEngine>>reportBlock: e GAEngine>>visualizeBlock: para passar os blocos para popular as abas adicionais.

Nota: Não esqueça de criar os accessors para reportBlock e visualizeBlock (trivial).

Mostramos abaixo a aba Custom view que mostra em vermelho os trajetos antes que o trajeto de menor tempo (em verde) seja obtido.

A última linha da aba Report mostra o indivíduo com melhor desenpenho.

Podemos clicar de novo na aba Custom view.

O método GARefraction>>calculateFitnessPoint:toPoint:speeds:with: calcula o desempenho calculando o “tempo” que o raio de luz leva para fazer o trajeto. O objetivo é minimizar este tempo (Princípio de Fermat).

GARefraction>>visualizeLogs: é o método que desenha numa das abas do inspector o trajeto do raio de luz.

Vamos introduzir mais uma coluna no nosso relatório. Calcularemos o coeficiente que envolve os senos dos ângulos de entrada em cada superfície e as velocidades. Os senos destes ângulos e a velocidade devem ser proporcionais (Lei de Snell). Dividir a proporção do senos pela das velocidades deve dar um resultado próximo ou igual a 1. O método GARefraction>>calculateSnellCoeficientsFor: abaixo calcula o par de índices.

Contatamos abaixo que os índices que criamos se aproximam de 1 nas duas superfícies.

Para que o meu exemplo funcione você precisa fazer os file ins abaixo.

A seguir o código da classe GARefraction.

'From Pharo7.0.3 of 28 June 2019 [Build information: Pharo-7.0.3+build.162.sha.814fd1b03d3d717fcd9032f374203c3047ca2870 (64 Bit)] on 10 July 2019 at 7:24:12.345899 am'!
Object subclass: #GARefraction
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GeneticAlgorithm-Core'!

!GARefraction methodsFor: 'as yet unclassified' stamp: 'chicoary 7/10/2019 07:07'!
calculateFitnessPoint: pointBelow toPoint: pointAbove speeds: speeds with: genes 
	| speedBelow speedAbove distanceBelow distanceAbove timeBelow timeAbove speedMiddle surfacePointBelow surfacePointAbove distanceMiddle timeMiddle |
	surfacePointBelow := 		genes first @ -0.5.
	surfacePointAbove :=  	genes second @ 0.5.
	speedBelow := speeds first.
	speedMiddle := speeds second.
	speedAbove := speeds third.
	distanceBelow := pointBelow distanceTo: surfacePointBelow.
	distanceMiddle := surfacePointBelow distanceTo: surfacePointAbove.
	distanceAbove :=  surfacePointAbove distanceTo: pointAbove.
	timeBelow := distanceBelow / speedBelow.
	timeMiddle := distanceMiddle / speedMiddle.
	timeAbove := distanceAbove / speedAbove.
	^ timeBelow + timeMiddle + timeAbove! !

!GARefraction methodsFor: 'as yet unclassified' stamp: 'chicoary 7/9/2019 11:40'!
surfaceBelowDataset
	| surfaceBelowDataset |
	surfaceBelowDataset := RTData new.
	surfaceBelowDataset connectColor: Color white.
	surfaceBelowDataset noDot.
	surfaceBelowDataset points: (0 to: 1 by: 0.01).
	surfaceBelowDataset y: [ 😡 | -0.5 ].
	^ surfaceBelowDataset! !

!GARefraction methodsFor: 'as yet unclassified' stamp: 'chicoary 7/9/2019 14:43'!
yBlockXBelow: xSurfaceBelow andAbove: xSurfaceAbove

	| lineBlock |
	lineBlock := [ :point1 :point2  😡 | point1 y + ((point2 y - point1 y) / (point2 x - point1 x) * (x - point1 x)) ].

	^ [ 😡 | 
	x <= xSurfaceBelow
		ifTrue: [ lineBlock value: 0@(-1.5) value: xSurfaceBelow@(-0.5) value: x ]
		ifFalse: [ x <= xSurfaceAbove
				ifTrue:
					[ lineBlock value: xSurfaceBelow@(-0.5) value: xSurfaceAbove@(0.5) value: x ]
				ifFalse: [ lineBlock value: xSurfaceAbove@(0.5) value: 1@(1.5) value: x ] ] ]! !

!GARefraction methodsFor: 'as yet unclassified' stamp: 'chicoary 7/9/2019 11:40'!
surfaceAboveDataset
	| surfaceAboveDataset |
	surfaceAboveDataset := RTData new.
	surfaceAboveDataset connectColor: Color white.
	surfaceAboveDataset noDot.
	surfaceAboveDataset points: (0 to: 1 by: 0.01).
	surfaceAboveDataset y: [ 😡 | 0.5 ].
	^ surfaceAboveDataset! !

!GARefraction methodsFor: 'as yet unclassified' stamp: 'chicoary 7/9/2019 18:28'!
calculateSnellCoeficientsFor: genes 
	| tanBelow tanAbove xBelow xAbove tanMiddle sinBelow sinMiddle sinAbove speedBelow speedMiddle speedAbove k1 k2 |
	xBelow := genes at: 1.
	xAbove := genes at: 2.
	tanBelow := xBelow.
	tanMiddle := xAbove - xBelow.
	tanAbove := 1 - xAbove.
	sinBelow := 1.0 / (1 + (1 / tanBelow squared)) sqrt.
	sinMiddle := 1.0 / (1 + (1 / tanMiddle squared)) sqrt.
	sinAbove := 1.0 / (1 + (1 / tanAbove squared)) sqrt.
	speedBelow := 0.2.
	speedMiddle := 1.0.
	speedAbove := 1.8.
	k1 := sinBelow / sinMiddle * speedMiddle / speedBelow.
	k2 := sinMiddle / sinAbove * speedAbove / speedMiddle.
	^ { k1 . k2 }! !

!GARefraction methodsFor: 'as yet unclassified' stamp: 'chicoary 7/9/2019 14:48'!
visualizeLogs: logs
	| g surfaceAboveDataset surfaceBelowDataset |
	g := RTGrapher new.
	
	logs allButLast do: [ :log | | rayDataset |
		rayDataset := self rayDatasetOn: log fittestIndividual genes.
		g add: rayDataset
	].
   g add: ((self rayDatasetOn: logs last fittestIndividual genes) connectColor: Color green).  
	
	surfaceAboveDataset := self surfaceAboveDataset.
	g add: surfaceAboveDataset.
	surfaceBelowDataset := self surfaceBelowDataset.
	g add: surfaceBelowDataset.
	^ g! !

!GARefraction methodsFor: 'as yet unclassified' stamp: 'chicoary 7/9/2019 14:49'!
rayDatasetOn: genes
	| xSurfaceAbove y rayDataset xSurfaceBelow |
	rayDataset := RTData new.
	rayDataset connectColor: Color red.
	rayDataset noDot.
	xSurfaceBelow := genes first.
	xSurfaceAbove := genes second.
	y := self yBlockXBelow: xSurfaceBelow andAbove: xSurfaceAbove.
	rayDataset points: (0 to: 1 by: 0.01).
	rayDataset y: [ 😡 | y value: x ].
	^ rayDataset! !

Abaixo o método GAEngine class>>exampleRefraction.

'From Pharo7.0.3 of 28 June 2019 [Build information: Pharo-7.0.3+build.162.sha.814fd1b03d3d717fcd9032f374203c3047ca2870 (64 Bit)] on 9 July 2019 at 6:52:38.306844 pm'!

!GAEngine class methodsFor: 'accessing' stamp: 'chicoary 7/9/2019 18:42'!
exampleRefraction
	| g reportBlock refractionCalculator visualizeBlock fitnessBlock |
	refractionCalculator := GARefraction new.
	g := self new.
	g endIfNoImprovementFor: 10.
	g minimizeComparator.
	g populationSize: 100.
	g numberOfGenes: 2.
	
	g createGeneBlock: [ :rand :index :ind | rand next ].
	
	fitnessBlock := [ :genes | 
			refractionCalculator
				calculateFitnessPoint: 0.0 @ -1.5
				toPoint: 1.0 @ 1.5
				speeds: {0.2 . 1.0 . 1.8}
				with: genes 
	].
	reportBlock := [ :log | 
		{
			#genes -> log fittestIndividual genes.
			#fitness -> log fittestIndividual fitness. 
			#snell -> (refractionCalculator calculateSnellCoeficientsFor: log fittestIndividual genes). 
		} 
	].
	visualizeBlock := [ :logs | refractionCalculator visualizeLogs: logs ].
	
	g fitnessBlock: fitnessBlock.
	g reportBlock: reportBlock.
	g visualizeBlock: visualizeBlock.
	g run.
	g inspect! !

Os métodos introduzidos em GAEngine para acrescentar as abas Custom view e Report no Inspector seguem abaixo:

'From Pharo7.0.3 of 28 June 2019 [Build information: Pharo-7.0.3+build.162.sha.814fd1b03d3d717fcd9032f374203c3047ca2870 (64 Bit)] on 9 July 2019 at 6:59:18.496397 pm'!

!GAEngine methodsFor: 'gt-inspector-extension' stamp: 'chicoary 7/9/2019 11:41'!
gtInspectorViewIn2: composite
    <gtInspectorPresentationOrder: -10&gt;
    composite roassal2
        title: 'Custom view';
        initializeView: [ self visualize2 ]! !

'From Pharo7.0.3 of 28 June 2019 [Build information: Pharo-7.0.3+build.162.sha.814fd1b03d3d717fcd9032f374203c3047ca2870 (64 Bit)] on 9 July 2019 at 6:59:39.332449 pm'!

!GAEngine methodsFor: 'events-roassal' stamp: 'chicoary 7/9/2019 11:20'!
visualize2
	visualizeBlock ifNotNil: [
   		^ self visualizeBlock value: logs 
	]! !

'From Pharo7.0.3 of 28 June 2019 [Build information: Pharo-7.0.3+build.162.sha.814fd1b03d3d717fcd9032f374203c3047ca2870 (64 Bit)] on 9 July 2019 at 6:59:18.480491 pm'!

!GAEngine methodsFor: 'gt-inspector-extension' stamp: 'chicoary 7/8/2019 15:09'!
gtInspectorReportIn: composite
    <gtInspectorPresentationOrder: -1&gt;
    composite list
        title: 'Report';
        display: [ self report ]! !

'From Pharo7.0.3 of 28 June 2019 [Build information: Pharo-7.0.3+build.162.sha.814fd1b03d3d717fcd9032f374203c3047ca2870 (64 Bit)] on 9 July 2019 at 6:59:39.348127 pm'!

!GAEngine methodsFor: 'events-roassal' stamp: 'chicoary 7/8/2019 15:29'!
report
	^ self logs collect: [ :log | 
		self reportBlock value: log
	].! !

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s