Você está na página 1de 133

UNIVERSIDADE PRESBITERIANA MACKENZIE

PROGRAMA DE PÓS-GRADUAÇÃO EM
ENGENHARIA ELÉTRICA

Fabio Razzo Galuppo


fabiogaluppo@acm.org

http://member.acm.org/∼fabiogaluppo

RESOLUÇÕES DO PROBLEMA DO CAIXEIRO VIAJANTE


APLICANDO ALGORITMOS DE APROXIMAÇÃO,
RANDOMIZAÇÃO E HEURÍSTICAS DA INTELIGÊNCIA
ARTIFICIAL COM COMPUTACÃO PARALELA

Dissertação apresentada ao Programa


de Pós-Graduação em Engenharia Elétrica da
Universidade Presbiteriana Mackenzie, para a
obtenção do tı́tulo de Mestre em Engenharia
Elétrica.

Orientador: Prof. Dr. Nizam Omar

São Paulo
2013
AGRADECIMENTOS

Desejo agradecer minha famı́lia pelo apoio dado até o momento: Anna Maria Razzo
Galuppo (mãe), Fernando Razzo Galuppo (irmão) e Janete Teodoro Leite (esposa). Em
especial, a Francisco Galuppo (pai, in memoriam), por ter me incentivado e inspirado no
belo caminho da Computação e da Matemática.

A Universidade Presbiteriana Mackenzie, por ter concedido uma bolsa CAPES-PROSUP,


o que permitiu a conclusão do curso, a pesquisa e a elaboração dessa dissertação.

Aos professores do Programa de Pós-Graduação Stricto Sensu da Engenharia Elétrica.


Em especial, ao meu orientador Prof. Dr. Nizam Omar, pelo excelente direcionamento,
orientação e interação no programa.

Aos meus amigos, Luis Fábio Mandina Pereira - simplesmente por ser um segundo
irmão e amigo de longa data, e Rogério Wagner - por ser outro grande amigo e ter me
incentivado a procurar um programa de Mestrado em Ciência da Computação. Gostaria
de estender estes agradecimentos aos meus amigos e colegas do Mestrado da UPM, dos
grupos de usuários (em especial, do C/C++ Brasil) e da BM&F Bovespa, seria uma lista
extensa se eu colocasse o nome de todos eles, porém o mais importante é que eles sabe
quem são.

A todos os Cientistas da Computação e Engenheiros de Software que se dedicam e


contribuem com a expansão dessas áreas. Aos grandes personagens da Computação e da
Matemática. Em especial, a Bjarne Stroustrup, pela linguagem de programação C++. A
Alexander Stepanov, pela Standard Template Library (STL) do C++. A Don Syme, pela
linguagem de programação F#. A Martin Odersky, pela linguagem de programação Scala.
A Donald Knuth, pela obra The Art of Computer Programming. A Robert Sedgewick por
disseminar a disciplina de Algoritmos de uma maneira acessı́vel. A John McCarthy, Alan
Turing e Alonzo Church pelas contribuições e obras ligadas a Inteligência Artificial e a
Programação Funcional.

Ao Coursera, Udacity, Museu da Matemática e toda instituição e profissionais que


trabalham em prol da disseminação do ensino, principalmente da Computação e da Ma-
temática.
“The more you know, the more you learn; the more you learn, the more you
can do; the more you can do, the more the opportunity...”

Dr. Richard W. Hamming


RESUMO

Esta obra tem como essência a aplicação das técnicas denominadas coletivamente de me-
taheurı́stica paralela no contexto do Problema do Caixeiro Viajante (PCV), um dos pro-
blemas de otimização combinatória mais importantes. A abordagem desta obra contém
uma proposta composicional que permite a criação de pipelines para endereçar o problema.
Estas técnicas extraı́das da Computação Paralela associadas aos algoritmos de busca da
Inteligência Artificial possibilitam grandes oportunidades para a exploração do espaço
de estados do problema em questão. Usando as combinações propostas, boas soluções
ou, até mesmo ótimas soluções, emergirão dentro de um tempo de processamento sa-
tisfatório, possibilitando suas aplicações na resolução de problemas reais semelhantes. É
fundamental revisitar as soluções existentes e fornecer para a indústria as melhores opções
para resolução do PCV utilizando as capacidades computacionais contemporâneas e as
variedades de equipamentos disponı́veis. Nesta obra, estão incluı́dos a implementação, a
análise e a medição de algoritmos aplicados ao contexto referenciado.

Palavras-chave: computação paralela, computação concorrente, algoritmos,


desempenho e otimização de algoritmos, metaheurı́stica, metaheurı́stica paralela,
inteligência artificial, problema do caixeiro viajante e otimização combinatória.
ABSTRACT

This work has as its essence the application of techniques collectively called parallel me-
taheuristic in the context of a Travelling Salesman Problem (TSP), one of the most
important problems in combinatorial optimization. The approach of this work contains
a compositional proposal that allows the creation of pipelines to address the problem.
These techniques extracted from the Parallel Computing associated with the search algo-
rithms of Artificial Intelligence allow great opportunities for exploring the state space of
the problem in question. Using the proposed combinations, good solutions or even opti-
mal solutions will emerge within a satisfactory processing time, allowing its application in
real-world problems. It is essential to revisit the existing solutions and provide the best
alternatives for the industry to solve the TSP using contemporary computing capabilities
and varieties of available equipments. In this work, are included the implementation,
analysis and measurement algorithms applied to the referenced context.

Palavras-chave: parallel computing, concurrent computing, algorithms, performance


and algorithms optimization, metaheuristic, parallel metaheuristic, artificial intelligence,
travelling salesman problem, and combinatory optimization.
Sumário
1 INTRODUÇÃO 1
1.1 O Problema do Caixeiro Viajante . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Motivação para a Pesquisa . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Estado da Arte na Solução do Problema . . . . . . . . . . . . . . . . . . . 2
1.4 Proposta de Pesquisa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.5 Objetivos da Pesquisa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.6 Metodologia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.7 Próximos Capı́tulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2 O PROBLEMA DO CAIXEIRO VIAJANTE 7

3 RESOLUÇÃO DE SISTEMAS COMPLEXOS 16


3.1 Resolução Metaheurı́stica por Simulated Annealing (SA) . . . . . . . . . . 19
3.2 Resolução Metaheurı́stica por Algoritmo Genético (GA) . . . . . . . . . . . 22
3.3 Resolução Metaheurı́stica por Otimização por Colônia de Formigas (ACO) 25
3.4 Resolução Heurı́stica por Busca Local 2-OPT . . . . . . . . . . . . . . . . 28

4 DESEMPENHO E ALGORITMOS PARALELOS 29


4.1 Fork/Join para distribuir, explorar e selecionar . . . . . . . . . . . . . . . 32

5 MODELO COMPOSICIONAL PARA RESOLUÇÃO DO PCV 35

6 SOLUÇÕES PROPOSTAS 43
6.1 Resolução Metaheurı́stica por Simulated Annealing (SA) e 2-OPT com
Computação Paralela . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
6.2 Resolução Metaheurı́stica por Algoritmo Genético (GA) e 2-OPT com
Computação Paralela . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.3 Resolução Metaheurı́stica por Otimização por Colônia de Formigas (ACO)
e 2-OPT com Computação Paralela . . . . . . . . . . . . . . . . . . . . . . 48
6.4 Considerações sobre o Modelo Composicional . . . . . . . . . . . . . . . . . 49

7 RESULTADOS OBTIDOS 51
7.1 Resultados obtidos com o Pipeline 1 (SA) . . . . . . . . . . . . . . . . . . 51
7.2 Comportamento monotonicamente decrescente . . . . . . . . . . . . . . . . 57
7.3 Resultados obtidos em uma distribuição aleatória com 250 cidades . . . . . 59
7.4 Resultados obtidos com o Pipeline 2 (GA) . . . . . . . . . . . . . . . . . . 62
7.5 Resultados obtidos com o Pipeline 3 (ACO) . . . . . . . . . . . . . . . . . 65

8 CONCLUSÃO E TRABALHOS FUTUROS 69

REFERÊNCIAS BIBLIOGRÁFICAS 76

ANEXOS 77
1 INTRODUÇÃO

As técnicas de Computação Paralela associadas aos algoritmos de busca da Inteligência


Artificial que exploram um espaço de estados através da aplicação de metaheurı́stica
permitem a geração de uma maior quantidade de soluções candidatas na resolução de
problemas com dimensões consideradas intratáveis. Problemas da classe NP, como o
Problema do Caixeiro Viajante, possui esta caracterı́stica. Usando estas combinações, de
uma maneira composicional, uma boa solução ou, em alguns casos, uma ótima solução
emergirá dentro de um tempo de processamento satisfatório para aplicações na solução
de problemas reais e complexos.

1.1 O Problema do Caixeiro Viajante

O Problema do Caixeiro Viajante (PCV) é um dos mais clássicos e desafiadores


problemas computacionais de roteamento envolvendo um grafo com pesos. Ele conta
com um grande número de aplicações práticas em diversos segmentos da indústria, tais
como, logı́stica, genética, manufatura, telecomunicação e neurociência. Ele tem inspirado
pesquisas e estudos por matemáticos, cientistas da computação, engenheiros, quı́micos,
fı́sicos, psicólogos, educadores, pesquisadores não profissionais, entre outros (APPLE-
GATE, 2007).

O PCV consiste na determinação do menor ciclo ou circuito para um conjunto de


cidades a serem visitadas obrigatoriamente. Isto significa, que a partir de uma determi-
nada cidade inicial (ou vértice do grafo), todas as cidades (os outros vértices deste grafo)
serão visitadas uma única vez, exceto a cidade de origem, por sua vez, também é a cidade
de destino, denotando assim, um ciclo. O custo de viagem entre um par de cidades é
o peso associado as arestas que conectam o grafo. Neste caso, o objetivo é encontrar o
caminho menos custoso para visitar todas as cidades, ao final, retornando a sua origem. O
PCV busca pelo circuito hamiltoniano com o peso (custo ou comprimento) total mı́nimo
(COOK, 2011).

Apesar da compreensão do PCV ser simples, ele é um dos problemas mais comple-
xos (GAREY, 1979a) e intesivamente investigados em Matemática Computacional. Sua
simplicidade e intratabilidade tornam o PCV um modelo ideal para o desenvolvimento

1
de idéias e técnicas para a investigação de problemas computacionais em geral (APPLE-
GATE, 2007).

1.2 Motivação para a Pesquisa

A aplicação dos algoritmos da Inteligência Artificial (IA) para resolução de proble-


mas complexos podem e devem ser expandidos através das capacidades da Computação
Paralela e os recursos computacionais existentes. Existe um grande número de problemas
complexos que não possuem uma solução exata ou ótima devido as suas dimensões e re-
quisitos. Esta classe de problemas intratáveis, denominados polinômiais não-determinı́sco
(NP), são modelos computacionais interessantes a serem investigados. Dentre eles, o Pro-
blema do Caixeiro Viajante (PCV), ao qual é um problema de roteamento onde existem,
e provavelmente sempre existirão, instâncias em aberto. Ele oferece uma ótima fundação
para exploração e aplicação dos algoritmos de IA em paralelo.

1.3 Estado da Arte na Solução do Problema

O Problema de Caixeiro Viajante (PCV), por se tratar de uma referência amplamente


investigada da classe dos problemas decidı́veis e intratáveis, conta com diversas soluções
envolvendo a união entre heurı́sticas e paralelismo. As pesquisas mais recentes envolvendo
os tipos de proposta alinhadas com esta obra, abordam a solução do PCV diretamente
ou indiretamente. Elas consistem em: aceleração através de busca local utilizando GPUs
(ROCKI, 2012), aplicação de Simulated Annealing (SA) em paralelo utilizando busca
independente ou colaborativa (CZECH, 2010), aceleração em paralelismo de dados para
casos de Otimização por Colônia de Formigas (ACO) (CECILIA, 2013) e paralelismo com
Algoritmos Genético (AG) (GOMEZ, 2009). No entanto, estas abordagens buscam uma
boa solução dentro de um tempo de processamento satisfatório e exploram paralelismo
para atingir maiores desempenhos e/ou um número maior de soluções candidatas.

Uma outra abordagem, que não é o foco principal desta obra, é a busca das soluções
ótimas do PCV (TSP@GATECH, 2007). No entanto, estas soluções podem demorar anos
para serem encontradas.

2
1.4 Proposta de Pesquisa

A proposta de pesquisa desta obra está alinhada com os objetivos descritos na próxima
seção. Ela inclui a implementação, a análise e a medição dos algoritmos relacionados a
seguir: Simulated Annealing (SA), Algoritmos Genético (AG), Otimização por Colônia
de Formigas (ACO) e Otimização por Busca Local (OBL).

Os algoritmos selecionados serão implementados juntamente com técnicas de com-


putação paralela. Isto significa que eles serão implementados suportando paralelismo
através do uso de CPUs, porém com possibilidades de serem estendidos futuramente
para GPUs ou outros processadores computacionais. Esta decisão dependerá das carac-
terı́sticas do algoritmo selecionado. Eles serão aplicados na resolução do Problema do
Caixeiro Viajante (PCV) e verificados de acordo com as estratégias já conhecidas, como
por exemplo, testados com os dados públicos da TSPLIB (REINELT, 1991a).

Além disso, um dos destaques é a apresentação de um modelo composicional para a


resolução do PCV juntamente com um framework para seu desenvolvimento, inspirado
em diversas técnicas, dentre elas, o modelo estrutural do algoritmo genético.

1.5 Objetivos da Pesquisa

Os objetivos desta obra são estabelecer conexões entre as técnicas de busca da Inte-
ligência Artificial que utilizam metaheurı́stica e a aplicação dos recursos e das abstrações
oferecidas através da Computação Paralela. Bem como, analisar e medir o comportamento
destes algoritmos isolados ou na forma de composição utilizando diversas configurações de
processamento. O objetivo central é implementar os algoritmos selecionados e aplicá-los
ao Problema do Caixeiro Viajante de acordo com os detalhes apresentados nesta obra.

3
1.6 Metodologia

Para explorar o PCV através da perspectiva dos algoritmos abordados em IA jun-


tamente com as capacidades da computação paralela, foi selecionado um conjunto de
métodos metaheurı́sticos e outras heurı́sticas de otimização, conforme descrito nos capı́tulos
anteriores. Elas serviram para análise e base comparativa. O problema intratável em
questão, o PCV, possui um grande potencial exploratório através de técnicas de parale-
lismo computacional. Os desafios ou estruturas computacioniais presentes nesta seleção
de algoritmos, ou seja, os requisitos de aplicação neles contidos, envolvem: grades não
estruturadas, álgebra linear, cálculos estatı́sticos, aleatoriedade e travesia de grafos (ASA-
NOVIC, 2009).

As principais fontes determinantes na escolha dos algoritmos e aquisição de conheci-


mento foram os artigos e os livros relacionados nesta obra. Após a investigação e seleção
dos algoritmos, eles foram implementados usando uma linguagem de programação mul-
tiparadigma e com suporte a paralelismo. Pelo desempenho, portabilidade, controle e
familiaridade do autor, C++ (C++, 2011) é a linguagem de programação escolhida para
a implementação dos algoritmos paralelos que foram analisados. Por ser uma linguagem
que permite construções sucintas e também de familiaridade do autor, F# (FSHARP,
2013) foi aplicada na apresentação de algum conceito ou raciocı́nio que não precisará ser
medido ou avaliado em detalhes, para outros casos foram adotados pseudocódigos.

As análises e representações dos dados produzidos foram feitos através das ferramentas
Wolfram Mathematica e/ou Microsoft Excel. Dentre os recursos de computação paralela
não distribuı́da, foram utilizados uma combinação de threads na CPU e as extensões de
processamento vetorial existentes nos processadores modernos. Logo, o principal objetivo
foi extrair o máximo e medir o comportamento dos recursos computacionais presentes
nestes equipamentos multicores (ASANOVIC, 2009) para entender o comportamento re-
lacionado a exploração do espaço de estados do problema em questão.

Na coleta de dados foram utilizados marcadores e pontos bem definidos que permiti-
ram a verificação de desempenho de um determinado algoritmo em relação ao tempo de
processamento e/ou a qualidade do resultado(s) gerado(s). Alguns dos algoritmos selecio-
nados foram executados e medidos em um trabalho conjunto através de algum modelo de
composição, como por exemplo, em uma cadeia de execução (pipeline). Em alguns casos,

4
somente um pseudocódigo possuem as informações relativas ao contexto do problema ou
a um eventual esclarecimento.

Os dados que foram utilizados para os testes com o PCV simétrico e euclidiano bidi-
mensional foram obtidos: a) aleatoriamente através de geradores de números randômicos
ou b) de fontes já existentes, tais como, TSPLIB (REINELT, 1991a) ou TSP Test Data
(TSP@GATECH, 2009).

Para execução dos testes e suas respectivas análises foram utilizados os recursos com-
putacionais de acordo com as especificações indicadas na figura 1. Todos os equipamentos
são de propriedade do autor:

Figura 1: Configuração dos computadores para medições e coleta de dados

Foram reportados os principais procedimentos dos testes. Isso foi obtido através dos
indicadores, tais como: gráficos contextuais, tabelas e grafos denotando os ciclos encon-
trados. Todos os códigos fontes produzidos e utilizados estão relacionados e encontram-se
na seção de anexos deste trabalho.

5
1.7 Próximos Capı́tulos

A seguir é apresentada a organização desta obra.

O capı́tulo 2 descreve o Problema do Caixeiro Viajante (PCV): suas representações,


seus desafios e como as heurı́sticas da Inteligência Artificial (IA) podem ajudar no es-
tabelecimento de uma solução satisfatória. Neste capı́tulo, ainda são apresentados a
complexidade e a explosão combinatória gerado por uma abordagem sequencial e de força
bruta para encontrar uma ótima solução do PCV simétrico.

O capı́tulo 3 discorre sobre algoritmos e suas propriedades. Neste capı́tulo, também é


descrito sobre complexidade computacional e as classes polinômias (P) e polinômias não-
determinı́sticas (NP). É apresentado uma alternativa de resolução para o PCV através da
aplicação de heurı́sticas da IA.

O capı́tulo 4 estabelece a noção sobre desempenho computacional, a utilização de


algoritmos paralelos para obtenção de desempenho e os novos tipos de hardwares com
capacidades de computação paralela. Além disso, este capı́tulo cita como problemas
intratáveis, como no caso do PCV, podem se beneficiar de uma classe de heurı́stica, ao
qual coletivamente é chamada de metaheurı́stica paralela.

O capı́tulo 5 apresenta a construção monádica para abordar o PCV. Neste capı́tulo,


é introduzido o modelo composicional juntamente com suas propriedades.

O capı́tulo 6 é o concentrador das soluções propostas desta obra e complementa o


capı́tulo 5, onde são apresentadas as soluções propostas da dissertação: a resolução do
PCV por Simulated Annealing (SA) com Computação Paralela, a resolução do PCV por
Algoritmo Genético (GA) com Computação Paralela e a resolução do PCV por Otimização
por Colônia de Formigas (ACO) com Computação Paralela. Para todas estas soluções
são aplicadas um heurı́stica de ajuste baseada em Otimização por Busca Local (OBL).

O capı́tulo 7 apresenta os resultados obtidos, a comparação com outros resultados


publicados e a conclusão do modelo proposto.

6
2 O PROBLEMA DO CAIXEIRO VIAJANTE

O Problema do Caixeiro Viajante (PCV) é um dos mais clássicos e desafiadores pro-


blemas computacionais de roteamento envolvendo um grafo com pesos, contando com um
grande número de aplicações práticas - onde se incluem instâncias do problema que en-
volvem grafos direcionados ou digrafos, como, por exemplo, a representação de ruas com
mão única (problema na forma assimétrica) e mão dupla (problema na forma simétrica).

O PCV consiste na determinação do menor ciclo ou circuito para um conjunto de ci-


dades a serem visitadas obrigatoriamente. Isto significa, que a partir de uma determinada
cidade inicial (ou vértice do grafo), todas as cidades (os outros vértices deste grafo) serão
visitadas uma única vez, exceto a cidade de origem, por sua vez, também é a cidade de
destino, denotando assim, um ciclo. O PCV busca pelo circuito hamiltoniano com o peso
(custo ou comprimento) total mı́nimo.

Figura 2: Homenagem do XKCD ao quase centenário Problema do Caixeiro Viajante.


(http://xkcd.com/399/)

O diagrama a seguir, indicado na figura 3, exibe um grafo não-direcionado completo


com cinco vértices e seus respectivos pesos. Tal disposição representa uma configuração
do PCV:

7
Figura 3: Grafo não-direcionado com cinco vértices e seus respectivos pesos.

A origem do PCV remete a um jogo inventado em 1857 por Sir William Rowan
Hamiltom, criador da matemática dos quaternions, o Jogo Icosiano (APPLEGATE, 2007).
Este jogo tinha como objetivo encontrar um ciclo através de 20 vértices distribuidos
em quatro pentágonos concêntricos, onde cada vértice continha três adjacentes (algumas
destas conexões ligando um pentágono mais externo a outro mais interno). No plano
tridimensional, a figura geométrica do jogo Icosiano é um dodecaedro. Em outro momento,
o tipo de solução oferecida pelo desafio do jogo fora denominada de ciclo hamiltoniano,
em homenagem ao seu criador. No entanto, somente no ano de 1930, o problema se
caracterizou como PCV por Merrill Flood da Universidade de Princeton e da RAND
Corporation. E seu desafio continua até os dias atuais. Sendo assim, o modelo abstrato
do PCV é de um problema de otimização combinatória relacionado a determinação de um
ciclo hamiltoniano em um grafo qualquer (direcionado ou não), cujo objetivo é encontrar
um ciclo de peso (custo ou comprimento) mı́nimo, ou seja, um problema de minimização
(APPLEGATE, 2007).

É possı́vel representar uma das soluções ou um dos ciclos de uma instância do problema
PCV, utilizando subgrafos, matrizes de adjacência, listas de adjacência ou até mesmo ca-
minhos de uma estrutura de dados do tipo árvore, onde o nó final contém intrı́nsecamente
uma referência ao nó inicial. Uma vez que resultados variados são derivados da confi-

8
guração de um PCV, este resultado, ou seja, o ciclo estabelecido como solução, também
pode ser denominado como uma instância da solução do PCV proposto.

A formulação do PCV como um problema de programação binária foi apresentada


em 1954 por George Bernard Dantzig, Delbert Ray Fulkerson e Selmer Martin Johnson
(DANTZIG, 1954). Na figura 4, é apresentado a modelagem matemática com as variáveis
de decisão e as restrições do problema:

Figura 4: Modelo do PCV através de Programação Binária. (Adaptação do modelo


proposto por Belfiore e Fávero (BELFIORE, 2013))

9
As restrições do problema indicadas na figura 4 em (1) e (2) determinam que cada nó
seja visitado uma única vez e a restrição (3) impede que ocorra a formação de sub-rotas.

Abaixo há duas representações alternativas de uma instância da solução do PCV


referente a figura 3. À esquerda, encontra-se um subgrafo. À direita uma matriz de
adjacências equivalente ao subgrafo anterior, ao qual os elementos formadores do ciclo
estão em destaque em suas respectivas células, elas podem ser interpretadas pela horizontal
ou pela vertical.

Figura 5: Subgrafo e matriz de adjacências equivalente.

Portanto, a notação sequencial do ciclo, equivalente a representação acima é: A - p1


- B - p2 - C- p3 - D - p4 - E - p0 - A.

Um algoritmo de resolução, escrito na linguagem de programação F# (FSHARP,


2013), do PCV utilizando uma abordagem por força bruta, ou seja, todas as possı́veis
combinações de caminhos, é apresentado a seguir:

10
Figura 6: Resolução do PCV através de força bruta.

O exemplo em questão, através da função graphToTree, transforma um grafo em uma


árvore. E a função bruteForceApproach, executa a combinação e a ordenação crescente
pela distância total desta combinação.

Assumindo que os valores das conexões correspondem a tabela indicada na figura 7:

11
Figura 7: Valores de referência para exemplo do PCV através de força bruta.

O resultado desta combinação está indicado a seguir:

Figura 8: Resultado para exemplo do PCV simétrico através de força bruta.

No entanto, a pergunta a ser respondida é: Quantos ciclos precisariam ser verificados
para resolver este problema para N cidades (ou vértices)?

O PCV é um problema de otimização combinatória, onde a explosão em seu caso


Γ(N )
simétrico é equivalente a 2 permutações. Este resultado é extraido do fato de um ciclo
em uma determinada ordem e sua reversa possuı́rem o mesmo valor total (destacados na

12
figura 8). Isto é, através da teoria da complexidade computacional, significa que ele é um
problema decidı́vel e classificado como polinomial não-determinı́stico (NP) da classe NP-
Completo ou NP-Difı́cil, de acordo com as métricas utilizadas (GAREY, 1979b). Neste
caso, dependendo do poder computacional, é possı́vel resolver por força bruta instâncias
do problema com máximo entre 20 a 30 cidades.

Figura 9: Métricas para resolução PCV com força bruta.

Outro fator a ser considerado é a representação do problema. Certamente, matrizes


de adjacência ou todas as combinações do grafo se forem mantidos em memória, podem
encontrar como barreira a capacidade de armazenamento destas informações, no caso do
número de cidades a serem visitadas for muito grande. Neste caso, e em situações contendo
um grande número de combinações, elas deveriam ser particionadas e distribuı́das. Porém,
dado sua ordem NP-Completo, encontrar uma solução ótima pode ser impossı́vel com o
poder computacional atual. De fato, existem abordagens por programação linear, entre
outras técnicas, que permitem encontrar uma das soluções ótima (caso exista mais de
uma possibilidade), porém em alguns casos para alcançar esta este resultado, requer anos
de processamento (TSP@GATECH, 2007), que pode ser (ou é) inviável na aplicação
para solucionar problemas reais - onde muitas vezes estes caminhos são dinâmicos. Por
exemplo, a rota feita pelo entregador de jornal pode ficar estática por um bom tempo, até

13
que um novo assinante apareça, se este for auxiliado por um algortimo para determinar
suas visitas, este algoritmo deve ser eficiente e adaptativo, ou seja, fornecer um boa rota
dentro de um perı́odo aceitável de tempo.

Existem declaradas soluções reais, utilizando uma abordagem algoritmica clássica da


Árvore Geradora Mı́nima (AGM), que pode encontrar uma solução quase ótima, se tiver
tempo suficiente para processamento. Por exemplo, uma semana para resolver um pro-
blema estático (MENDES, 2012). Para os outros casos, principalmente onde existe a res-
trição de tempo, para obter uma boa solução, a heurı́stica é aplicada. Atualmente, o maior
PCV com cidades já resolvidos contém uma solução ótima é inferior a 25 mil vértices.
(MENDES, 2012)(TSP@GATECH, 2005). E o máximo reportado sobre uma solução
ótima em uma aplicação VLSI encontra-se em torno de 86 mil vértices (TSP@GATECH,
2007).

Através do uso de heurı́sticas, não há garantias, mas uma solução tende a convergir
para um bom resultado com complexidade de tempo polinomial (P). No caso do PCV, uma
heurı́stica aceitável é do vizinho mais próximo. Ela é uma técnica de algoritmo guloso,
onde a partir de uma determinada cidade, a próxima será a cidade não visitada com menor
distância entre elas. Este tipo de abordagem possui complexidade algoritimica de O(N2 ).
É uma solução eficiente, embora, assim como o Hill Climbling (RUSSELL, 2009), ela pode
ficar presa no mı́nimo local e seu sucesso depende muito da forma do espaço de estados
associado. Para superar este aspecto do algoritmo, pode ser utilizada uma heurı́stica
de troca de vizinhos, onde usualmente é aplicado algum comportamento aleatório para
determinar quais os vizinhos serão trocados. Neste caso, é imprático manter todos as
instâncias previamente encontradas, o que corre no risco destas instâncias serem visitadas
novamente, portanto o espaço de busca voltará a ser Γ(N ). Isto também é válido para os
casos assimétricos do PCV.

Sobretudo, tentativas no desenvolvimento de heurı́sticas genéricas, ou seja, que sirvam


para endereçar uma variedade de problemas, não tiveram muito sucesso até o desenvol-
vimento de soluções baseadas em Inteligência Artificial (IA), coletivamente denominadas
soluções metaheurı́sticas (VOSS, 2006). Estas soluções são geralmente utilizadas na re-
solução de problemas aos quais não existe conhecimento de um algoritmo eficiente para
resolver o problema, sendo uma boa solução considerada aceitável. As soluções de IA

14
com metaheurı́sticas são basicamente técnicas inteligentes de busca, onde algumas delas
são baseadas em sistemas adaptativos naturais. Para alguns problemas de otimização
combinatória, como no caso do PCV, é possı́vel resolvê-los de uma maneira menos que
perfeita, porém com um considerável valor prático ou aplicado. Sendo, os tipos de al-
goritmos implementados para este propósito chamados de algoritmos de aproximação -
comuns em soluções IA que utilizam metaheurı́stica. Eles estão baseados no fato de que
em muitos casos uma solução pouco menos que ótima, dentro de uma escala de tempo
satisfatória, é melhor que nenhuma solução (HAREL, 2012b).

Em resumo, o PCV é um problema de roteamento intratável da ordem polinomial


não determinı́stica (NP). Conforme descrito nas páginas 211 e 212 do COMPUTERS
AND INTRACTABILITY de Garey e Johnson (GAREY, 1979a), ele é um problema NP-
Completo, porém pode ser classificado como NP-Difı́cil se for considerado a investigação
da versão geométrica com métrica Euclidiana. Uma instância do problema com 48 cida-
des (ou vértices) em sua configuração simétrica terá uma combinação de 2, 58623 × 1059
possibilidades ou 47 fatorial (47!). Em sua configuração assimétrica esta mesma instância
terá uma combinação de 1, 24139 × 1060 possibilidades ou 48 fatorial (48!). A figura 10
ilustra uma descrição sucinta do problema:

Figura 10: Resumo do PCV.

15
3 RESOLUÇÃO DE SISTEMAS COMPLEXOS

Algoritmo é uma prescrição, onde um conjunto de instruções finitas e bem definidas,


que tem por objetivo a resolução de um problema. Computacionalmente, um algoritmo
possui propriedades intrı́nsicas relacionadas com complexidade de desempenho e espaço
consumido em memória. Estas propriedades são classificadas de acordo com uma ordem
de crescimento para determinar o comportamento assintótico do algoritmo. Para entender
a eficiência e o comportamento dos algoritmos são necessários saber sua complexidade e
seu modelo computacional.

A classificação do algoritmo é dado numa linguagem matemática, organizada em


ordem crescente (Figura 11), por: constante (k), logarı́tmica (log N), linear (N), linear-
logarı́tmica (N log N), quadrática (N2 ), cúbica (N3 ) e exponencial (2N ).

Figura 11: Classificação da ordem de crescimento em escala logarı́tmica proposta por


Sedgewick (SEDGEWICK, 2009).

16
A seguir são apresentadas algumas tarefas que representam operações de ordem cres-
cente descritas anteriormente (Figura 11). Elas estão em formas de funções acumuladoras,
codificadas na linguagem de programação F# (FSHARP, 2013).

Abaixo as funções acumuladoras: constante (constant), logarı́tmico (logarithmic), li-


near (linear ), linear-logarı́tmico (linear-logarithmic), quadrático (quadratic), cúbico (cu-
bic) e exponential (exponencial ). A função exponencial utiliza internamente a geração
do coeficiente binomial para produzir uma sequência iterativa equivalente ao triângulo de
Pascal. Estas funções representam esforços que são tratáveis ou intratáveis na medida
em que a quantidade de elementos computáveis aumenta através de um comportamento
assintótico.

Figura 12: Funções acumuladoras e suas ordens de crescimento.

17
Obtendo a classificação com relação a sua ordem de crescimento, é possı́vel estimar
qual o comportamento e o tempo de processamento de um determinado algoritmo se o
número de elementos computáveis crescer. Ou seja, o objetivo é saber as caracteristicas
do algoritmo com base numa determinada medida de eficiência (na maioria dos casos, o
tempo de processamento - velocidade) e em quanto tempo um resultado (correto) será
produzido. No entanto, há alguns problemas que não possuem uma solução eficiente
conhecida, tais problemas são denominados NP-completo (CORMEN, 2009). Com isto,
uma hierarquia de complexidade emerge.

A escala de complexidade computacional considera como problemas polinômias (P)


aqueles que possuem um algoritmo eficiente com ordem inferior nos limites da ordem expo-
nencial e, em alguns casos, de potências com expoentes elevados. No caso onde existe um
algoritmo eficiente para o problema, ele é considerado tratável. E, no geral, os problemas
intratáveis não possuem um algoritmo eficiente. Mesmo alguns dos problemas tratáveis
podem degradar dependendo da quantidade de elementos a ser processado, ou seja, o
poder computacional contemporâneo pode ser um outro limite que impeça a resolução do
problema.

O PCV é um problema intratável e classificado como polinomial não-determinı́stico


(NP). Existem soluções ótimas para o PCV com cidades, porém o maior deles já resolvido
contém uma solução abaixo de 30 mil cidades (MENDES, 2012)(TSP@GATECH, 2005),
levando anos de processamento para encontrar este resultado (TSP@GATECH, 2004).

Uma alternativa de resolução para o PCV é através da aplicação de heurı́sticas, onde


uma solução pode convergir para um bom resultado, ou seja, encontrar uma solução sub-
ótima - isto não é garantido, mas por outro lado pode ser o melhor resultado em relação
as restrições de tempo estabelecidas. Este é o trade-off que deve ser considerado na
adoção de tais técnicas. No caso do PCV, a abordagem é utilizar as técnicas de busca
com heurı́stica para guiar e limitar o processo de busca em um espaço de estados com
uma dimensão intratável.

18
3.1 Resolução Metaheurı́stica por Simulated Annealing (SA)

Uma das técnicas de IA baseada na combinação de fı́sica estatı́stica, no método de


Monte Carlo e em um modelo estocástico do algoritmo Hill Climbing (HC) é a Simulated
Annealing (SA). Onde, no artigo original, o problema referencial para a demostração do
algoritmo é o PCV com 400 cidades (KIRKPATRICK, 1983)(KIRKPATRICK, 1988).

O SA é um modelo que simula o processo fı́sico de agregação de partı́culas em um


sistema submetido em uma temperatura altı́ssima, onde no decorrer do tempo a tem-
peratura vai caindo lentamente até o seu resfriamento dado um determinado critério -
esta caracterı́stica do sistema é denominada de cooling schedule. Neste processo, ocorre o
realinhamento das moléculas e o ajuste do sistema. Na metalurgia, no processo de anne-
aling, os metais são submetidos a fornos para serem temperados, portanto, as moléculas
do metal são recombinadas para obterem maior resistência.

O SA é uma busca probabilı́stica, ao contrário do HC não estocástico, ele aplica mo-


vimentos não intuitos e dinamismo em sua parametrização como, por exemplo, aceitar
soluções inferiores à solução atual para não ficar preso, dependendo do tipo de problema,
no mı́nimo ou máximo local. Este comportamento é comum ocorrer no inı́cio da solução
onde a temperatura é elevada. No entanto, durante o resfriamento, o sistema vai se estabi-
lizando, consequentemente havendo uma maior resistência em aceitar soluções inferiores.
A troca de energia do sistema neste caso é análoga a troca de vizinhos no PCV. O SA é
baseado no algoritmo de Metropolis-Hastings (ROBERT, 2009), a aceitação ou rejeição
de uma nova solução, inferior a anterior, é baseada na aplicação da função de Boltzmann.
Este comportamento ocorre durante os passos da iteração mantidas no processo de cooling
schedule, exceto quando um novo resultado com valor melhor não for encontrado através
da avaliação da função objetivo. No caso do PCV, uma função objetivo que avalia se o
novo ciclo é maior ou igual a solução atual ou a diferença de energia entre o novo resultado
e o resultado atual não for menor do que zero. Basicamente a função objetivo e a função
de Boltzmann determinam os critérios de aceite do resultado obtido.

O SA possui um comportamento exploratório, isto significa que ele pretende encontrar


ou visitar soluções em regiões não exploradas no espaço de estados. Sendo uma solução
estocástica, baseada no conceito de algoritmos de aproximação, ela depende dos aspectos
aleatórios inerentes a caracterı́stica da operação. No caso, a implementação computacio-

19
nal, depende primariamente da qualidade do gerador de números randômicos, ao qual tem
uma influência positiva ou negativa sobre os resultados da solução (KLIMASAUSKAS,
2002). Benoı̂t Mandelbrot, criador da matemática dos fractais, associa a qualidade de
aleatoriedade com um coeficiente, denominado de coeficiente de Hurst (H). O H indica
um ı́ndice de dependência relativo a uma série de valores no decorrer do tempo (série tem-
poral). Se o H estiver entre maior que 0.5 e 1, significa que existe um alta probalidade de
um número alto (ou baixo) ser seguido por outro número de mesma caracterı́stica (com-
portamento persistente). Se o valor estiver entre 0 e menor que 0.5 significa que existe
um alta probabilidade de um alternação entre os número da série, se o número atual é
um valor alto, o próximo será baixo, ou vice e versa (comportamento anti-persistente).
No entanto, se H for igual a 0.5, o comportamento da sequência é incerta, portanto im-
previsı́vel (MANDELBROT, 2004) - o que é muito bom para a caracterı́stica exploratória
da solução com SA.

O Problema do Caixeiro Viajante é amplamente estudado pela Pesquisa Operacional


e pela Ciência da Computação. Provavelmente, o SA não é a abordagem mais eficiente
se comparado com outras heurı́sticas. Para ele ser competitivo será necessário ajustes
finos em sua parametrização (SKISCIM, 1983). Mesmo com um esforço computacional
um pouco maior se comparado com outras heurı́sticas, o SA é um bom algoritmo para
poder escapar dos mı́nimos locais. Por outro lado, existem diversas abordagens aplicando
paralelismo denominadas coletivamente de Parallel Simulated Annealing (PSA) aos quais
permitem um balanceamento entre os trade-offs (AYDIN, 2005). Um tipo de abordagem
paralela do SA é descrita mais adiante nesta obra.

20
A função SIMULATED-ANNEALING apresenta em pseudocódigo o método do SA
descrito anteriormente:

Figura 13: Função SIMULATED-ANNEALING.

21
3.2 Resolução Metaheurı́stica por Algoritmo Genético (GA)

Algoritmo genético (GA) pertence a uma classe de algoritmos evolutivos da Inte-


ligência Artificial (IA). A técnica do GA foi adaptada com o objetivo de encontrar soluções
aproximadas para os problemas de otimização e busca tais como o PCV. Assim como o
SA, esta é outra técnica inspirada na natureza, neste caso, ela é baseada em alguns or-
ganismos biológicos que sofrem sua evolução através do processo da seleção natural e
da sobrevivência do ”mais forte”ou mais apto (fittest) (HOLLAND, 1992b), segundo os
princı́pios estabelecidos pelo darwinismo (HOLLAND, 1992a). De forma abstrata, o GA
utiliza do processo referenciado os conceitos de: reprodução, hereditariedade, variação,
recombinação e seleção natural (MITCHELL, 2003). Portanto, deste modelo biológico o
algoritmo aplica as idéias de população de cromossomos, seleção natural para cruzamento,
geração de descendentes através de crossover e mutação para obter diversidade.

Usualmente, a representação da solução do problema em GA é feita por uma string


binária, ou seja, ela é uma sequência de zeros e uns, ao qual essa string é denominada
cromossomo, sendo um agrupamento de genes (contendo os 0s e os 1s). No entanto,
no caso do PCV, isso não é uma boa representação ou, pelo menos, uma representação
expressiva que forneça alguma vantagem para endereçar as caracterı́sticas estruturais do
PCV. Segundo Zbigniew Michalewicz, optar por uma string binária é uma péssima escolha
para representar soluções do PCV (MICHALEWICZ, 2010).

Para o PCV, a representação selecionada é a do caminho (path representation), onde


cada vértice do grafo, exceto o primeiro e o último do ciclo, estão conectados aos vizi-
nhos da esquerda e da direita - facilitando a aplicação da operação de crossover. Esta
representação de caminho é organizado numa estrutura de dados do tipo vetor (ou array),
onde o vizinhos da esquerda e da direita do elemento encontrado na posição N, estarão
nas posições N-1 e N+1 respectivamente.

A avaliação de um resultado superior é feita por uma função de aptidão (ou de fitness)
ou função de recompensa. No caso do PCV, a função objetivo do problema é baseada no
tipo de problema a ser investigado, por exemplo, em sua versão simétrica e bidimensional,
o valor a ser medido pode ser a distância euclidiana. Esta função de fitness poderá
indicar mais do que se o problema foi resolvido ou não, ela servirá também para avaliar
qual a proximidade de uma solução ideal, orientando no processo de seleção e geração de

22
populações com soluções candidatas ao PCV.

No algoritmo de GA é encontrado, de uma forma adaptada ao problema, três opera-


dores genéticos populares: a seleção, a recombinação (ou crossover ) e a mutação.

A operação de seleção é responsável pela escolha dos indivı́duos (ou das soluções
candidatas do PCV) que formarão a próxima população.

A operação de recombinação é responsável pela produção de descendentes a partir do


compartilhamento de material genético (no caso do PCV, organização entre os vizinhos).
Um dos processos válidos é a seleção de dois indivı́duos da população com as melhores
funções de fitness, denominados de pais, e estes são manipulados para gerar filhos com
uma função de fitness superior, sendo no PCV, uma solução de circuito mı́nimo). No
PCV, esta manipulação envolve a troca aleatória de cidades (ou vértices) entre duas das
soluções candidatas do PCV, porém é necessário manter a consistência e a estrutura do
ciclo.

A operação de mutação é responsável pela inversão ou troca de duas cidades (ou


vértices) da solução candidata do PCV. Normalmente, esta operação ocorre com proba-
bilidade aleatória após uma operação de recombinação.

Os procedimentos adotados no GA desta obra envolve:

1. No inı́cio, a criação de um conjunto aleatório de circuitos do PCV que formam as


soluções candidatas da primeira geração. A abordagem para a população inicial é
gulosa, ou seja, da preferência para as cidades (ou vértices) mais próximas uma das
outras;

2. A seleção das duas melhores soluções candidatas (com menor ciclo) na população.
Elas serão os pais, onde serão recombinado para gerar duas novas soluções candidatas
ou dois novos filhos. Esses filhos poderão, na maioria das vezes, serem melhores que
os pais;

3. Baseado numa condição aleatória os filhos gerados anteriormente sofrerão mutação.


Isto é para impedir que possam ocorrer semelhantes soluções candidatas entre a
população;

4. Os novos filhos substituirão na população existente as soluções candidatas com re-

23
sultados inferiores (com maior ciclo).

Durante o processamento do GA, o tamanho da população ou a quantidade de soluções


candidatas do PCV mantém-se inalterada. Além disso, este processo é iterativo. Os
parâmetros do tamanho da população e da quantidade de interações são definidos de
acordo com um balanceamento do tamanho do problema a ser solucionado e o tempo
de execução (aproximadamente) desejado. A função GENETIC-ALGORITHM apresenta
em pseudocódigo o método do GA descrito anteriormente:

Figura 14: Função GENETIC-ALGORITHM.

24
3.3 Resolução Metaheurı́stica por Otimização por Colônia de
Formigas (ACO)

Otimização por colônia de formigas (ACO), bem como o SA e GA descritos anterior-


mente, é outro método de otimização inspirado na natureza, neste caso, ele foi baseado na
observação do comportamento de formigas reais atuando em colônias (CECILIA, 2013).
Mesmo não sendo as criaturas mais inteligentes deste planeta, todavia sendo um inseto
social, as formigas apresentam uma notável inteligência coletiva enquanto colônia. A
inteligência exposta pelas colônias de formigas é um exemplo de comportamento emer-
gente (LUCCI, 2012). Comportamento emergente significa que um grupo de agentes ou
entidades simples, no caso as formigas, conseguem atuar em um ambiente, formando
comportamentos complexos em seu coletivo. O comportamento das formigas é gover-
nado pela sobrevivência da colônia ao invés da sobrevivência dos indı́viduos, sendo esta
caracterı́stica uma das peças centrais do algoritmo desta seção.

O algoritmo do ACO pode ser aplicado como heurı́stica por uma grande quantidade de
problemas (DORIGO, 2006)(BLUM, 2005), principalmente aqueles do tipo que possuem
uma estrutura naturalmente modelada como um grafo. A primeira aplicação do ACO foi
com o PCV, em sua forma original denominada de Ant System (AS) (DORIGO, 1996).

No ACO, em essência, as formigas artificiais ou simuladas geram soluções para instâncias


do PCV em forma de tours ou ciclos. Essas formigas são simplesmente agentes que
possuem a capacidade de construir tours de forma paralela e probabilı́stica. Elas são
orientadas nesse processo por uma trilha virtual de feromônio (pheromone trail ) e outras
informações pertinentes a heurı́stica. Este rastro de feromônio é um elemento fundamental
no algoritmo, ele é o facilitador ou estimulador da coordenação indireta entre os agen-
tes e o ambiente, onde este processo é denominado estigmergia (stigmergy) (DORIGO,
1997)(DORIGO, 2004).

O processo de estigmergia atua da seguinte forma em um grafo do PCV: um conjunto


de formigas virtuais são distribuı́das em torno deste grafo. As formigas selecionam, de
forma probabilı́stica, seus caminhos baseados na intensidade de feromônio onde as arestas
ou conexões do grafo não estejam ocupadas ou foram visitadas previamente. Quanto
maior o nı́vel de feromônio em uma conexão, maior a probabilidade dela ser escolhida.

25
Quando as formigas completam um ciclo, a função de fitness é avaliada e o nı́vel de
feromônio do grafo é atualizado. Ao passar do tempo, as melhores arestas do grafo, que
determinarão um ciclo mı́nimo possuirão um nı́vel de feromônio maior, portanto existerá
a convergência, ou seja o maior número de formigas percorrendo o mesmo circuito.

Existem duas ações atuando sob o nı́vel de feromônio depositado no grafo são elas:
a intesificação e a evaporação. As ações de intensificação e evaporação são regidas pelas
seguintes equações:

Figura 15: Equações da distribuição, intensificação e evaporação do feromônio.

26
Outra equação que faz parte do ACO é a de probabilidade para a seleção de caminho,
onde o objetivo é definir qual será a próxima cidade (ou vértice) visitada. Ela é obtida
através da relação a seguir:

Figura 16: Equação de probabilidade para seleção do caminho.

A função ANT-COLONY-OPTIMIZATION apresenta em pseudocódigo o método do


ACO descrito anteriormente:

Figura 17: Função ANT-COLONY-OPTIMIZATION.

27
3.4 Resolução Heurı́stica por Busca Local 2-OPT

A heurı́stica de busca local 2-Opt para resolução do PCV (CROES, 1958) serve para
ajustar um circuito de uma instância do problema. Esta heurı́stica busca o melhor resul-
tado local, eliminando os cruzamentos entre as arestas. Percorrendo todos os pares das
arestas não adjacentes e alterando suas conexões é possı́vel alcançar o mı́nimo local do
circuito, conforme descrito nos passos do algoritmo em pseudocódigo a seguir:

Figura 18: Funções 2-OPT-SWAP e 2-OPT-ALL. Elas compõem a heurı́stica 2-Opt.

28
4 DESEMPENHO E ALGORITMOS PARALELOS

A preocupação, ou o questionamento sobre o desempenho computacional em relação


ao tempo, ocorre desde o século XIX, quando foi concebida a Máquina Análitica de Charles
Babbage:

“As soon as an Analytic Engine exists, it will necessarily guide the future
course of the science. Whenever any result is sought by its aid, the question
will arise - By what course of calculation can these results be arrived at by the
machine in the shortest time? (BABBAGE, 1864)”

Todo trabalho de análise matemática de algoritmos é fundamentado e baseado nas


operações sequenciais, onde as instruções não podem ocorrer no mesmo tempo. Este mo-
delo é conhecido como arquitetura de von Neumann ou arquitetura de Princeton (NEU-
MANN, 1993).

A Arquitetura de von Neumann ainda é a arquitetura dominante na construção de


computadores convencionais. Na construção de um algoritmo, existem duas propriedades
fundamentais a serem consideradas e medidas: tempo de processamento e espaço dos
dados. Uma vez determinado o espaço consumido por um algoritmo sequencial, isto torna-
se uma caracterı́stica imutável, sua variação de tempo de processamento será proporcional
a sua complexidade e a velocidade do processador. No entanto, existe a possibilidade
da construção de algoritmos usando técnicas de computação paralela para aumentar o
desempenho em relação ao tempo de processamento e, consequentemente em muitos casos,
consumindo mais espaço de dados (embora em um intervalo menor de tempo).

Atualmente, para executar mais computações por segundo, os processadores estão


sendo desenvolvidos com mais de um núcleo de processamento, denominados de processa-
dores multicore, em outras palavras eles são um tipo de computador paralelo. Portanto,
para obter o melhor desempenho destes computadores, será necessário explorar algoritmos
usando técnicas de computação paralela, ou simplesmente paralelismo (CORMEN, 2009).

Segundo David Harel, no livro Algorithmics, paralelismo tem se tornado cada vez mais
crucial, em grande parte por causa da tendência da construção destes novos hardwares
(processadores com capacidade de computação paralela). Onde computadores com quatro

29
núcleos são comuns hoje em dia. Para tomar vantagem destes tipos de processadores,
novos algoritmos e técnicas de programação são necessárias. Uma das técnicas populares
é a map-reduce, inspirada pela programação funcional (HAREL, 2012a). Onde o objetivo
é dividir, conquistar e muitas vezes consolidar, isto é também conhecido como fork-join
ou divide-conquer. Por exemplo, o MERGE-SORT (CORMEN, 2009), é estruturado
adequadamente para a aplicação desta técnica:

Figura 19: Pseudocódigo do MERGE-SORT, algoritmo criado por von Neumann, e adap-
tado de Cormen (CORMEN, 2009).

A técnica de map-reduce, determina que a computação de um grande agrupamento


de dados seja dividida em partes, onde estas partes são executadas separadamente e os
resultados são combinados com a utilização de uma função de acumulação (HAREL,
2012a). No caso do MERGE-SORT, a função recursivamente efetua a quebra do conjunto
de dados em partes que são executadas independentes, e a função MERGE tem por
objetivo consolidar o processamento das etapas previamente divididas. Este algoritmo na
versão sequencial implica, no pior caso, em uma complexidade de tempo igual a O(N LOG
N), e sua versão paralela a O(N) (HAREL, 2012a). Em contrapartida, versão paralela do
MERGE-SORT, exige um consumor maior de memória.

Problemas intratáveis, como no caso do PCV, podem se beneficiar de uma classe de


heurı́stica, ao qual coletivamente é chamada de metaheurı́stica paralela. Ela inclui os
tradicionais algoritmos de metaheurı́stica, como por exemplo, Simulated Annealing (SA),
Algoritmo Genético (GA), Otimização por Colônia de Formigas (ACO), entre outros.

30
Portanto, seu objetivo é a composição destes algoritmos com técnicas de computação pa-
ralela para reduzir o tempo de processamento e utilizar os diversos tipos de processadores
(CPUs e/ou GPUs) no máximo de suas capacidades.

Ao explorar o espaço de estados através buscas com SA, existem duas possı́veis abor-
dagens de busca com metaheurı́stica paralela: independente, onde cada entidade paralela
executa sua computação e procedimentos, ao final a melhor solução é selecionada; ou
cooperativa, onde etapas do algoritmo são separadas em fases e atuam em cadeia, as enti-
dades paralelas deste modelo trocam informações durante o processamento para selecionar
a melhor solução encontrada (CZECH, 2010). As buscas, independente e cooperativa, são
modelos aplicativos dos padrões de programação paralela Divide and Conquer e Pipeline
respectivamente. Estes padrões são descritos no capı́tulo 4 do Patterns for Parallel Pro-
gramming (MATTSON, 2004) ou em catálogos de padrões como o mantido pelo grupo do
Parallel Computing Laboratory da UC Berkeley (PARLAB@BERKELEY, 2010).

Uma variação do padrão Divide and Conquer é o Fork/Join, ele consiste no pro-
cesso de espalhar (dividir) uma tarefa em diversos segmentos paralelos para resolução do
problema (conquistar). Ao final, estas entidades paralelas já completadas são sincroni-
zadas. Com o MERGE-SORT, a última etapa é a execução do último MERGE e por
consequência a ordenação completa do vetor. Já no SA em paralelo com busca indepen-
dente, a última etapa é seleção da melhor solução encontrada no momento. Portanto,
ambos compartilham o mesmo tipo de abstração paralela para alcançar um desempenho
superior.

No caso do SA em paralelo fica evidente que o número de soluções candidatas au-


mentarão proporcionalmente ao número de entidades paralelas disponibilizadas para a
resolução do problema, permitindo tais soluções rodarem desde smartphones até super-
computadores. Então, também é necessário identificar qual a relação entre a quantidade
de entidades paralelas e a qualidade do resultado. Intuitivamente, é possı́vel interpretar
quanto maior for o paralelismo disponı́vel, melhor será a qualidade do resultado. Ou
seja, um comportamento, em teoria, monotonicamente decrescente, já que o PCV é um
problema de minimização. No entanto, para esta conclusão é necessária a verificação e
os testes em detalhes dos algoritmos propostos nesta obra em quantidades diversas de
alocações de processamento e tamanho do problema.

31
Outro modelo que está presente nos computadores atuais e deve ser considerado, é o
processamento vetorial. Este tipo de processamento está presente nos processadores con-
vencionais e processadores paralelos massivos, tais como, aceleradoras gráficas discretas
ou Graphical Processing Units (GPUs). A computação processada por este tipo de dis-
positivo é classificada através da taxonomia de Flynn como Single Instruction, Multiple
Data (SIMD) ou Multiple Instruction, Multiple Data (MIMD) (AKHTER, 2006).

A busca, a utilização e a criação do paralelismo é um dos maiores assuntos de pes-


quisa desde do inı́cio da década de 80. Onde a força motivadora para estas questões de
paralelismo, em hardware e software, é a necessidade insaciável por maior desempenho,
especialmente por uma alta velocidade computacional (TOSIC, 2004).

O objetivo desta obra é abordar o PCV por meio da composição dos algoritmos de
busca da Inteligência Artificial, da aplicação de paralelismo e da utilização de recursos
computacionais de alto-desempenho. Mapeando quais são as vantagens, ganhos e desafios
com relação a construção de algoritmos sequenciais. As abordagens aqui apresentadas per-
mitirão serem adaptadas ou servirem de base para resolução de outros tipos de problemas
intratáveis.

4.1 Fork/Join para distribuir, explorar e selecionar

Em geral, a aplicação do padrão Fork/Join tem como caracterı́stica a criação dinâmica


de tarefas (fork ) com a finalidade de resolver um problema e mais adiante a junção destas
tarefas (join) para dar continuidade na execução. Ele é muito comum de ser encontrado em
conjunto com outro padrão denominado Loop Parallelism (MATTSON, 2004). Padrões
como OpenMP ou ISO C++ 11 oferecem APIs ao qual a finalidade é a espalhar tarefas
computacionais para escalar o processamento. No OpenMP, em C++, isto é feito através
de diretivas (#pragma omp parallel for ) que delega ao compilador o trabalho da geração
do paralelismo e sincronização do ponto de junção.

32
Figura 20: Exemplo de paralelismo em C++ com a API do OpenMP 3.0 retirado do
padrão (OPENMP, 2008).

Usualmente, o Fork/Join é aplicado em situações de decomposição de tarefas ou de


dados (MATTSON, 2004), por exemplo, ao dividir tarefas para atuar em partições dos
dados e ao final consolidar. Em alguns casos particionando o espaço de estados a ser
explorado. De forma abstrata este padrão oferece o seguinte comportamento:

Figura 21: Modelo abstrato do Fork/Join.

33
No ISO C++ 11 (ISOCPP, 2011), a linguagem de programação adotada na imple-
mentação desta obra, é permitido um ajuste mais customizável e moderno na expressão
do paralelismo e da sincronização, isto é feito com uma combinação entre std::vector,
std::async e std::future, como ilustrado no fragmento a seguir:

Figura 22: Fragmento em C++ 11 ilustrativo a aplicação de std::vector, std::async e


std::future do functor ForkJoin da implementação proposta (detalhes no anexo).

A aplicação do padrão Fork/Join nesta obra objetiva a criação de diversas tarefas


explorando (preferencialmente) o maior número de estados distintos do espaço de esta-
dos de uma instância do PCV. Este trabalho ocorre em conjunto com os algoritmos de
inteligência artificial selecionados e as estratégias descritas no decorrer desta obra.

34
5 MODELO COMPOSICIONAL PARA RESOLUÇÃO
DO PCV

Como descrito nos capı́tulos 3 e 4 sobre as técnicas selecionadas nesta obra relativas
a resolução de sistemas complexos e aos padrões de computação paralela. Aqui será
apresentado o modelo proposto de resolução do PCV através de composição. A figura 23
denota de forma geral a idéia composicional proposta para abordagem do problema:

Figura 23: Composição entre algoritmos da Inteligência Artificial e Computação Paralela.

A composição aplicada aqui possui um relacionamento indireto com o conceito ma-


temático de composição de funções. A inspiração à aplicação de um modelo composi-
cional vem das Monads, baseadas em conceitos da Teoria das Categorias e introduzidas
na década de 90 na linguagem de programação funcional Haskell (WADLER, 1992)(WA-
DLER, 1995).

Uma monad é uma forma de estrutura computacional em termos de valores e sequências


de computação usando estes valores. Ela permite a definição de building blocks que são
sequências de computação, além disso ela determina como combinar computações e formar
uma nova, esse encadeamento é a composição, onde a saı́da de uma computação é a en-
trada de uma outra. Este processo, teoricamente, ocorre de forma indefinida, ou seja, cabe
ao implementador saber quando é o suficiente utilizar o valor retornado como resultado,
encerrando a sequência computacional. Com uma monad é permitido criar um pipeline
de execução. No caso do PCV, a solução proposta envolve uma monad que permitirá a
utilização de building blocks contextuais, aos quais sabem atuar sobre as instâncias do
PCV, onde habilitará a construção de um ou mais pipelines para resolver o problema.

De acordo com as definições pertinentes da Teoria das Categorias, a proposta inclui


uma coleção de objetos do tipo PCV e uma coleção de morfismos para satisfazer o mo-
delo composicional. O modelo de morfismo proposto é uma função, no caso um estrutura
computacional pré-definida, a qual sabe manipular, transformar ou atuar em um domı́nio

35
(PCV) e produzir um contradomı́nio (PCV). Em uma categoria esse tipo de morfismo re-
cebe o nome de endomorfismo. Esse endomorfismo poderá ser concretizado, por exemplo,
através da aplicação de uma função representando um algoritmo de SA. Ela receberá uma
instância do PCV com uma determinada configuração e produzirá um objeto destino, de
preferência, igual ou melhor ao objeto de origem.

A seguir a notação usada para os objetos e o(s) morfismo(s). Neste caso, uma operação
contendo objeto de origem, morfismo e objeto de destino:

Figura 24: Morfismo com objetos monádicos [M(TSP)].

A sequência a seguir é a notação de uma composição de objetos onde o objetivo é


estabelecer um pipeline para solução de uma instância do PCV. A operação de composição
satisfaz à propriedade associativa:

Figura 25: Composição com objetos monádicos [M(TSP)].

O tipo paramétrico Monad em Haskell possui uma interface contendo três membros
são eles: um construtor de tipo, uma operação para binding e uma função de unidade. O
construtor de tipo serve para obter ou instanciar o tipo monádico. A função de unidade
mapeia um valor simples para um valor do tipo monádico correspondente, no caso do PCV,
o que é deseja é um tipo PCV monádico, portanto uma estrutura de dados do tipo PCV
sofre uma aplicação de uma função de unidade para ser transformado em um tipo PCV

36
monádico. A operação de binding é responsável por mapear um valor monádico em outro,
onde poderá possuir o mesmo tipo do objeto de origem. Na figura 26 é apresentada a
classe Monad do Haskell e seus membros (HUGHES, 1998). É possı́vel interpretá-la como
uma abstração que encapsula um tipo e sua computação:

Figura 26: Monad, um tipo de dados abstrato de ações.

Na figura 26, o membro return é a função de unidade e o operador >>= representa a


operação de binding. O construtor de tipo é a própria Monad instanciada, por exemplo,
M TSP.

Para um tipo ser qualificado como monad torna-se essencial que três axiomas sejam
satisfeitos: o axioma da associatividade, o axioma da identidade à esquerda ou aplicação
da função de unidade à esquerda e o axioma da identidade à direita ou aplicação da função
de unidade à direita. Estes axiomas são conhecidos como as leis da monad (WADLER,
1995) e estão formalizados na notação do cálculo lambda (λ calculus) como segue:

Figura 27: As leis da Monad (Adaptação da formalização de Wadler (WADLER, 1995)).

Na linguagem Haskell existem diversos tipos de monads (HASKELL.ORG, 2011),

37
como por exemplo IO Monad que sabe lidar com computações que envolvem operações
E/S ou Error Monad que sabe lidar com computações que envolvem operações as quais po-
dem falhar ou disparar uma exceção. O conceito de monad em linguagens de programação
não é mais exclusivo da linguagem Haskell (MICROSOFT, 2013a)(MICROSOFT, 2013b)(SCALA-
LANG.ORG, 2013), estando presente cada vez mais em outras linguagens ou capacitadas
de ser reproduzidas, desde que existam construções fundamentais disponı́veis, como por
exemplo, high-order functions. Para esta obra foi concebido a TSP Monad, um tipo que
sabe lidar com o PCV de forma composicional. Seus principais membros, implementados
na linguagem de programação C++, são apresentados a seguir:

Figura 28: TSP Monad.

38
A TSP Monad proposta na figura 28 fornece um construtor de tipo, uma função
de unidade a partir da função ret e uma operação de binding a partir da função bnd.
Para evitar uma sintaxe convoluta, o método map foi adicionado - esta abordagem é
semelhante a construção da função flatMap da linguagem Scala (SCALA-LANG.ORG,
2013). Para fazer parte da categoria das monads é fundamental esse tipo obedecer as
três leis referenciadas na figura 27. As provas relativas as leis citadas são apresentadas
a seguir. Elas são apresentadas em duas versões, uma utilizando um notação funcional e
outra uma notação orientada a objetos:

Figura 29: TSP Monad e a primeira lei.

Figura 30: TSP Monad e a segunda lei.

39
Figura 31: TSP Monad e a terceira lei.

Portanto, conforme demonstrado, a TSP Monad (figura 28) é uma autêntica monad !

A monad proposta (figura 28) permitirá construções de pipelines computacionais para


resolução do PCV conforme vislumbrado na figura 23, ao qual poderão encadear uma
sequência de operações. Um comportamento similar ao padrão de projeto denominado
Cadeia de Responsabilidade (Chain-of-Responsibility pattern) (GAMMA, 1995)(RAJAN,
2010). No entanto, o padrão descrito através da orientação a objetos é verboso se for
comparado com o modelo monádico sugerido.

O método map ou a função bnd da TSP Monad são os pontos de entrada para os
morfismos, ou seja, são membros que recebem funções transformadoras - elas manipulam
uma instância da estrutura TSP e geram um tipo monádico TSP. Por exemplo, a cons-
trução de um pipeline simples contendo o encadeamento do Simulated Annealing (SA) e

40
do 2-OPT é descrito na figura 32 abaixo:

Figura 32: Pipeline com Simulated Annealing seguido por 2-OPT.

Se aplicado a uma instância do PCV, o pipeline indicado na figura 32, após processa-
mento, produzirá um resultado semelhante como segue:

Figura 33: Resultado da execução do Pipeline descrito na figura 32.

41
Os functors, ou as funções transformadoras, tais como para aplicação do Simulated
Annealing (functor SA) ou do 2-OPT (functor 2OPT ), também foram implementadas
para outros algoritmos e estruturas, como por exemplo, Otimização por Colônia de Formi-
gas, Algoritmo Genético, Fork/Join (fragmento apresentado na figura 22), entre outros.
Estes elementos permitirão a construção dos pipelines ou modelos composicionais para
explorar o PCV juntamente com Computação Paralela e Inteligência Artificial. O modelo
destas funções transformadoras seguem uma determinada assinatura correspondente ao
fragmento central da operação de binding, permitindo que outras funções sejam criadas
para agregar e participar de novas composições ou de alguma já existente. Abaixo dois
exemplos completos de functors simples descritos em C++:

Figura 34: Os functors para Identidade e 2-OPT.

Todos os componentes descritos no capı́tulo formam o modelo composicional proposto.


Eles atuarão de forma direta na definição das soluções propostas e na produção dos
resultados obtidos nesta obra.

42
6 SOLUÇÕES PROPOSTAS

A seguir, são apresentadas as propostas para resolução do PCV envolvendo algorit-


mos metaheurı́sticos e paralelismo. No capı́tulo anterior, foi descrito a proposta da TSP
Monad (figura 28) para construção de soluções composicionais, onde ela é um elemento
fundamental e complementar às soluções propostas neste capı́tulo. As resoluções apre-
sentadas aqui contemplam a aplicação de pipelines com o PCV, de acordo com o modelo
descrito anteriormente.

6.1 Resolução Metaheurı́stica por Simulated Annealing (SA) e


2-OPT com Computação Paralela

A resolução hı́brida desta seção possui uma composição monádica similar a indicada
na figura 25. Uma composição de SA e 2-OPT iniciada por uma heurı́stica construtiva, no
caso, do algoritmo do vizinho mais próximo, juntamente com computação paralela. Essa
composição produzirá uma notável forma de resolução hı́brida para explorar o PCV. Mo-
delos hı́bridos similares são adotados em problemas complexos a um certo tempo (AYDIN,
2005), mostrando ser um caminho promissor para exploração do problema.

O algoritmo do SA é bem estabelecido. Pode ser aplicado em situações reais e conta


com pesquisas em diversos segmentos que exploram suas caracterı́sticas (CHIBANTE,
2010). Ao final do processamento, apresenta uma boa solução dentro das restrições esta-
belecidas em sua parametrização e, principalmente, tem como destaque o fato de escapar
de mı́nimos (ou máximos) locais. No entanto, dado o contexto computacional atual, por
exemplo, as estações de trabalho, os servidores e até mesmo os smartphones existentes,
eles contam com processadores contendo dois ou mais núcleos de processamento. Porque
não utilizar este poder de processamento, uma vez que o SA e o 2-Opt são algoritmos
que demandam processamento intensivo (CPU bound )? Portanto, a idéia principal é uti-
lizar tal capacidade computacional para gerar um número maior de soluções candidatas.
A abordagem adotada para compor a solução hı́brida consiste em: iniciar o ajuste da
entrada do problema com a heurı́stica construtiva do vizinho mais próximo, executar
função de SA e em seguida aplicar a função de 2-OPT para ajustes entre as conexões.
As duas últimas etapas da resolução proposta devem obedecer uma relação composicional

43
conforme fora ilustrada na figura 25.

Após a aplicação da heurı́stica construtiva indicada, as ações indicadas, SA seguida


por 2-OPT, poderão ser distribuı́das em tarefas paralelas independentes e sem comu-
nicação entre elas, cujo este modelo é denominado multiple independent runs (MIR) (AY-
DIN, 2005). Ao final, haverá um ponto de sincronização do processamento concorrente
e/ou paralelo destas soluções independentes, onde a melhor solução do PCV, no caso
do probleme simétrico e euclidiano bidimensonal, o menor circuito a ser percorrido, será
extraı́da como o resultado do problema. O modelo denota a aplicação da técnica de IA
unificada a programação paralela com o objetivo de aumentar as opções de resolução de
um problema NP-Difı́cil - um PCV simétrico, no caso, se a solução ótima for encontrada
(GAREY, 1979a). A técnica de computação paralela aplicada aqui é inspirada em um
design pattern conhecido como Fork/Join, onde diversas tarefas são espalhadas para pro-
cessamento e ao término são sincronizadas para compor um resultado final (MATTSON,
2004).

As etapas indicadas na figura 25 serão executadas por iterações, onde o resultado se-
lecionado da iteração anterior será a entrada da nova iteração, exceto na primeira iteração
onde a instância do PCV entrado é gerado pela heuristica construtiva. Estas iterações
permitirão o ajuste ou a melhoria do resultado desejado com a compensação de um tempo
maior de processamento. O modelo elitista e iterativo referenciado aqui é inspirado da
estrutura do Algoritmo Genético (GA) (HOLLAND, 1992b). Um resumo da arquitetura
hı́brida em paralelo proposta é ilustrada na figura 36.

Figura 35: Pipeline 1.

A lei de Amdahl, estabelecida por Gene Ahmdal, determina teoricamente a melhor


velocidade (speedup) de um programa rodando em uma máquina com N núcleos de pro-
s+p
cessamento (AMDAHL, 1967). Isto é obtido através da equação: speedup = s+p/N , onde
”s”é o conjunto de instruções do programa que roda seqüencialmente (o que não é possı́vel
ser paralelizado), ”p”é o conjunto de instruções do programa que roda em paralelo e ”N”a
quantidade de núcleos de processamento. Se ”N”tender a infinito, o melhor speedup que

44
s+p
poderá ser alcançado é s (SUTTER, 2008). No caso da abordagem clássica do SA, a
porção sequencial do código, ou seja, as iterações existentes são dependentes - a iteração
externa determina o ajuste de temperatura do sistema, e a iteração interna determina
a atualização da solução candidata que utiliza o valor da temperatura, este trecho da
implementação não é trivial de ser adaptado para usar paralelismo, ficando limitado, em
termos de desempenho, a lei de Amdahl.

Figura 36: Solução 1. Correspondente ao Pipeline da figura 35.

No entanto, é possı́vel violar a lei de Amdahl, fazendo mais do mesmo trabalho, ou


seja, submetendo o sistema a uma sobrecarga de trabalho, ao qual possa explorar todo
o poder computacional da plataforma onde será executado. A lei de Amdahl apenas
considera o aumento do número de processadores numa carga de trabalho fixa então isto
pode (e deve) ser extrapolado (SUTTER, 2008). Uma boa estratégia para algoritmos de

45
aproximação, como no caso do SA, é colocar uma sobrecarga de processamento com o
objetivo de obter um número maior de soluções candidatas, ficando apenas com a melhor
delas no final. Esta estratégia, nas devidas proporções, é similar ao que está descrito
na seção ”Massive parallelism, scale-out, and speed ”do artigo sobre a implementação de
paralelismo (embarrassingly parallel ) no DeepQA da IBM, a infraestrutura de software
que roda por trás do supercomputador Watson ganhador do Jeopardy! (FERRUCCI,
2012).

A abordagem de extrapolação se mostrou eficiente pois, nos testes efetuados e indi-


cados adiante, as melhores soluções não apareceram necessariamente na primeira tarefa
lançada, demonstrando que a distribuição independente explora diversas áreas do espaço
de estados do problema, logo encontrando resultados mais interessantes.

6.2 Resolução Metaheurı́stica por Algoritmo Genético (GA) e


2-OPT com Computação Paralela

A solução proposta nesta seção possui a mesma estrutura da solução anterior, porém
ao invés do SA, ela é uma composição de GA e 2-OPT. Portanto, as mesmas considerações
serão válidas aqui. Ou seja, estruturalmente são equivalentes, exceto pelo comportamento
do algoritmo metaheurı́stico atuante no processo. Os elementos variantes são os valores
encontrados e o tempo de processamento, esses detalhes são indicados nos resultado ob-
tidos adiante. A seguir a composição da solução proposta:

Figura 37: Pipeline 2.

Um resumo desta arquitetura hı́brida em paralelo proposta é ilustrada na figura 38.

46
Figura 38: Solução 2. Correspondente ao Pipeline da figura 37.

O fragmento em C++ representa o código atual do pipeline indicado na figura 37:

Figura 39: Código correspondente ao Pipeline da figura 37.

47
6.3 Resolução Metaheurı́stica por Otimização por Colônia de
Formigas (ACO) e 2-OPT com Computação Paralela

A solução proposta nesta seção possui a mesma estrutura das soluções anteriores,
ao invés do SA ou do GA, ela é uma composição de ACO e 2-OPT. Contudo, as todas
as considerações anteriores serão válidas aqui. Os elementos variantes serão os valores
encontrados e o tempo de processamento. A seguir a composição da solução proposta:

Figura 40: Pipeline 3.

Um resumo desta arquitetura hı́brida em paralelo proposta é ilustrada na figura 41.

Figura 41: Solução 3. Correspondente ao Pipeline da figura 40.

48
6.4 Considerações sobre o Modelo Composicional

O modelo composicional proposto permite uma grande variação de construções para


criar resoluções do PCV, onde se destaca sua caracterı́stica de flexibilidade. Uma vez
obedecendo a assinatura das funções transformadoras, conforme indicadas na figura 34, é
possı́vel definir uma quantidade variada de estruturas ou adaptar outros algoritmos para
lidar com o PCV. Por exemplo, o functor Circular, apresentado a abaixo é uma estrutura
simples que implementa round-robin com o objetivo de executar functors variados:

Figura 42: O functor Circular.

Portanto, é possı́vel utilizá-lo para construir pipelines com processamento diversifi-


cado, como o exemplo hipotético a seguir:

Figura 43: Pipeline hipotético com o functor Circular.

É permitido ter pipelines que são composição de composições, conforme ilustrado no

49
exemplo abaixo:

Figura 44: Composição de Composições.

São construções válidas, a aplicação de pipelines contendo functors que utilizem pro-
cessamento heterogêneo e/ou execução adaptativa:

Figura 45: Pipeline hipotético com algoritmos executados em GPUs.

Figura 46: Pipeline hipotético com adaptabilidade.

Diante disso é possı́vel concluir que através do modelo proposto existe uma grande
flexibilidade para customização de soluções para explorar o PCV de forma simples ou
hı́brida. Conforme demonstrado neste capı́tulo, os modelos propostos são de resoluções
hı́bridas em paralelo.

50
7 RESULTADOS OBTIDOS

Nesta seção, estão descritos os resultados obtidos relacionados às soluções propostas
dos capı́tulos anteriores.

Algumas das abordagens para validação dos testes do PCV consistem na utilização da
geração aleatória das coordenadas ou na aplicação de cenários já conhecidos e organizados
na TSPLIB (REINELT, 1991b).

Os resultados apresentados nesta seção consistem nas duas estratégias citadas para
certificar a qualidade dos valores obtidos. As instâncias do PCV, aleatórias e seleciona-
das da TSPLIB, consistem na análise do problema em sua formulação simétrica, onde o
objetivo é obter a distância euclidiana bidimensional minı́ma do percurso. As instâncias
aleatórias utilizam uma distribuição uniforme para determinar onde serão posicionadas as
coordenadas das cidades (ou vértices) do problema. Enquanto, as instâncias da TSPLIB
possuem as coordenadas pré-definidas.

7.1 Resultados obtidos com o Pipeline 1 (SA)

Na figura 47 são apresentados alguns dos resultados obtidos em testes com proces-
samento paralelo da composição SA e 2-OPT, utilizando 32 tarefas independentes para
explorar a capacidade computacional de oito núcleos de processamento. Seis problemas
distintos da TSPLIB foram avaliados: cinco deles contendo 100 cidades (ou vértices) e um
com 48 cidades (ou vértices). Segundo os resultados apresentados nesta tabela, fica claro
concluir quando as instâncias são submetidas a um número de 32 tarefas paralelas e/ou
concorrentes, mesmo utilizando técnicas heurı́sticas onde o objetivo é buscar resultados
próximos dos valores ótimos com tempo de processamento satisfatório, após uma deter-
minada quantidade de iterações, este modelo hı́brido obtém o resultado ótimo indicado
na literatura.

51
Figura 47: Resultado do Pipeline 1 (figura 35) com 32 tarefas concorrentes.

Resultados semelhantes utilizando paralelismo nas diversas formas podem ser encon-
trados em outros trabalhos (O’NEIL, 2011). Os resultados apresentados na figura 47
são obtidos através do uso de CPUs, já os apresentados por O’Neil, Tamir e Burtscher
(O’NEIL, 2011) são através de GPUs. No entanto, ambas aplicam abordagens heurı́sticas
com paralelismo para alcançarem uma qualidade superior, no caso encontrar a solução
ótima das instâncias. No caso da instância kroE100 do PCV, catalogada na TSLIB, é
válido destacar que na versão hı́brida em paralelo, com SA e 2-OPT, assim como a des-
crita pelos outros autores (O’NEIL, 2011), ao qual utiliza Iterative Hill Climbing (IHC) e
2-OPT, foram necessários ajustes na configuração do algoritmo, pois o problema se desta-
cou como o mais desafiador em comparação aos outros relacionados na análise. Os valores
aplicados aos parâmetros do SA dos resultados apresentados na figura 47 são indicados
como segue:

52
Figura 48: Parâmetros atuais do algoritmo SA para as instâncias do PCV.

Os valor ótimo da instância kroE100 também é encontrado por Burtscher e Rabeti,


através da paralelização de alguns algoritmos de Iterative Local Searches (ILS). Contudo,
eles relatam que para alcançarem uma solução de ótima qualidade é necessário depender
de sorte (BURTSCHER, 2013) - certamente esta observação refere-se aos fatores aletórios
inerentes aplicada a solução. Eles reportaram que os melhores resultados, em termos de
tempo de processamento, da instância kroE100 são de aproximadamente 24 segundos.
No caso indicado na figura 47, em especı́fico na execução três, onde o resultado ótimo foi
encontrado na sexta iteração, a somatória do tempo de execução destas seis iterações foi
8,472 segundos - o que indica um bom desempenho da abordagem proposta.

53
Figura 49: Resultados da aplicação do modelo hı́brido em paralelo proposto com oito
tarefas.

Comparando os valores obtidos na instância kroB100 do PCV da TSPLIB em termos


de qualidade do circuito mı́nimo, os resultados apresentados na figura 47 indicam uma
melhoria em relação aos resultados das versões CPU e GPU com 2-OPT demonstrados por
Gazolla (GAZOLLA, 2010), onde os valores ficaram próximos da solução ótima em 0,17%
e 0,54% respectivamente. No caso, a solução proposta com menor números de tarefas
dedicadas a resolução do problema nem sempre encontraram a solução ótima, elas também
produzem bons resultados próximos da solução ótima, como estão indicados na figura 49.
O que evidencia, na maioria dos casos e relacionado com o problema em questão, se existir
um número maior de tarefas independentes, maiores serão as capacidades exploratórias
no espaço de estados - uma observação óbvia se os valores utilizados fossem invariantes,
mas não tão óbvias quando envolvem aleatoriedade.

No caso apresentado da instância kroE100 ficou claro a necessidade de um ajuste

54
fino em forma de argumentos dos métodos/funções ou através do número de iterações.
Este tipo de reconfiguração é exigida e deve ser analisada individualmente. Em uma
das verificações e validações do problema, um certo grupo de valores executados através
do modelo hı́brido proposto na instância att48 do PCV da TSPLIB apresentavam um
comportamento estacionário ao alcançar um mı́nimo local correspondente a 33.600,60 - as
soluções encontradas não ultrapassavam este valor. Porém, ao ser efetuada alterações ou
ajustes em seus parâmetros atuais, e conforme indicado na figura 50, o resultado ótimo foi
encontrado (33.523,70). A figura 50 destaca as conexões dissemelhantes entre os grafos.

Figura 50: Resultados da instância att48 do PCV da TSPLIB.

Todas as avaliações das instâncias do PCV da TSPLIB indicadas com a implementação


do modelo hı́brido em paralelo foram executadas em uma máquina com uma CPU Intel
Core i7-3770 de 3.40GHz com quatro núcleos de processamento, onde cada núcleo possui
dois processadores lógicos, totalizando oito processadores lógicos para os testes, junta-
mente com 12 GB de memória RAM instalada. Os testes foram produzidos nas confi-
gurações com duas, quatro, oito, 16 e 32 tarefas independentes em paralelas, cada uma
destas configurações apresentaram resultados variados. A figura 49 relaciona os resultados
obtidos com 8 tarefas.

55
Os outros resultados deste pipeline, contendo a execução de 16, quatro e duas tarefas
são apresentados a seguir:

Figura 51: Resultado do Pipeline 1 (figura 35) com 16 tarefas concorrentes.

56
Figura 52: Resultado do Pipeline 1 (figura 35) com 4 tarefas concorrentes.

Figura 53: Resultado do Pipeline 1 (figura 35) com 2 tarefas concorrentes.

7.2 Comportamento monotonicamente decrescente

Todos os testes passaram por um processo iterativo, ou seja, o melhor resultado


de uma iteração era a entrada da próxima iteração. Para manter um comportamento
monotonicamente decrescente, o sistema descartava resultados inferiores. Portanto, se a
entrada fosse um resultado ótimo a saı́da seria o mesmo resultado ótimo, ou um outro, caso
a solução tivesse mais do que um valor ótimo - no caso do PCV simétrico esta possibilidade

57
é verdadeira em relação a um percurso e a sua reversão. Os testes foram submetidos a 35
iterações, sendo que a iteração do melhor resultado selecionado está indicado na coluna
Iteração das tabelas 1 e 2. Por se tratar de uma solução que envolve aleatoriedade e da
qualidade do gerador randômico estes valores são suscetı́veis à variação.

Na comparação dos resultados através das 35 iterações de uma das soluções com as
configurações de oito e 32 tarefas em paralelo da instância kroA100 do PCV da TSPLIB
é possı́vel extrair um gráfico onde a curva é não monotônica, caso o modelo iterativo acei-
tasse valores inferiores entre as iterações. No entanto, é possı́vel obter a curva monotônica,
gerada pela seleção do melhor valor conhecido entre as iterações. As figuras 54 e 55 ilus-
tram estas iterações e suas respectivas curvas. Logo, as curvas da solução hı́brida em
paralelo indicadas nas figuras 54 e 55 são as indicadas pelo traçado pontilhado [kroA100
32 (1)* e kroA100 8 (1)* ] onde existe a sobreposição dos valores atuais [kroA100 32 (1)
e kroA100 8 (1)], tornando a solução proposta monoticamente decrescente entre as suas
iterações.

Figura 54: Curva monotonicamente decrescente (pontilhado) e Curva não-


monotonicamente decrescente (reta) para kroA100 com 32 tarefas.

58
Outro exemplo do comportamento monotonicamente decrescente é ilustrado na fi-
gura 55:

Figura 55: Curva monotonicamente decrescente (pontilhado) e Curva não-


monotonicamente decrescente (reta) para kroA100 com 8 tarefas.

7.3 Resultados obtidos em uma distribuição aleatória com 250


cidades

Uma outra série de testes para validação da qualidade da solução foram efetuadas
com a mesma distribuição de tarefas paralelas indicadas anteriormente. Neste caso, o
procedimento aplicado foi para 250 cidades (ou vértices) distribuı́das aleatoriamente, cujas
coordenadas bidimensionais variam por uma distribuição uniforme discreta entre 0 a 100.

A seguir é apresentado um dos grafos com valor inicial e final da solução hı́brida em
paralelo, este é um dos resultados extraı́do da solução indicada pelo valor da primeira
linha na coluna com 32 tarefas da figura 56. Na figura 57 é fácil notar que a configuração
inicial apresenta um grafo com uma distribuição de arestas que se cruzam por diversas
vezes, tornando densa a figura impressa. O grafo da melhor solução entre as candidatas
exibe uma caracterı́stica esparsa, não havendo cruzamentos entre o conjunto de arestas,
denotando em um resultado visivelmente superior.

59
Figura 56: Resultados das diversas execuções e configurações de paralelismo do PCV
aleatório com 250 cidades.

Figura 57: PCV aleatório com 250 cidades - configuração inicial e final da execução um
com 32 tarefas.

Para analisar o aspecto qualitativo da solução proposta, em termos da geração de


soluções candidatas, a medida em que ocorre um aumento de paralelismo para tratar o
problema, foi analisado cinco execuções distintas para uma instância do problema com
250 vértices variando entre duas a 32 tarefas paralelas e indicadas na figura 56, ao qual
foram extraı́dos a média, o desvio padrão e a distribuição normal em relação ao número
de tarefas dedicadas para a resolução do problema, figuras 58 e 59 respectivamente:

60
Figura 58: Média e Desvio Padrão dos testes do PCV aleatório com 250 cidades.

Figura 59: Distribuição normal dos testes do PCV aleatório com 250 cidades.

Portanto, uma interpretação dos resultados representados nas figuras 58 e 59 confir-


mam: quanto maior for o número de tarefas paralelas atacando o PCV de forma indepen-
dente, maior será a distribuição na exploração do espaço de estados e consequentemente
a convergência para um resultado melhor. É válido destacar que o uso do paralelismo
apresentado no artigo, busca demonstrar a capacidade de geração de um número maior
de soluções candidatas referentes a solução deste problema NP. Os resultados completos
dos testes apresentados podem ser encontrados no endereço: http://bit.ly/18z0sK6

61
7.4 Resultados obtidos com o Pipeline 2 (GA)

A seguir são apresentados os resultados de processamento do Pipeline 2 (figura 37),


os testes foram executados contra algumas instâncias do PCV definidas na TSPLIB:

Figura 60: Resultado do Pipeline 2 (figura 37) com 32 tarefas concorrentes.

Figura 61: Resultado do Pipeline 2 (figura 37) com 16 tarefas concorrentes.

62
Figura 62: Resultado do Pipeline 2 (figura 37) com 8 tarefas concorrentes.

Figura 63: Resultado do Pipeline 2 (figura 37) com 4 tarefas concorrentes.

63
Figura 64: Resultado do Pipeline 2 (figura 37) com 2 tarefas concorrentes.

Os resultados acima (figuras 60, 61, 62, 63 e 64) reforçam as observações e as inter-
pretações obtidas com os resultados apresentandos anteriormente através das avaliações
do pipeline 1. De um modo geral, quanto maior for o paralelismo aplicado na distri-
buição e exploração, melhores serão as possibilidades de resultados superiores emergirem
como solução. É possı́vel notar que os resultados com 32 tarefas são melhores do que
com 16 tarefas, e assim sucessivamente. Há casos onde a uma quantidade menor de
tarefas explorando o mesmo espaço de estados se saiu melhor, isto é por causa das abor-
dagens heurı́sticas não serem exatas, elas dependem de aleatoriedade e as vezes os valores
randômicos gerados não são tão bons. Outro ponto, é que o comportamento da abor-
dagem proposta é generalista, poderá haver casos de insucesso em outros cenários, pois
ajustar um pipeline, dependerá do tipo de problema, da seleção de um conjunto correto
de algoritmos e os ajustes finos em seus parâmetros. No entanto, os resultados atingidos
são significativos e a arquitetura do modelo composicional se mostrou eficiente gerando
bons resultados na abordagem dos problemas avaliados.

64
7.5 Resultados obtidos com o Pipeline 3 (ACO)

Abaixo encontra-se os resultados dos testes com o Pipeline 3 (figura 40).

Figura 65: Resultados do Pipeline 3 com a instância kroB200 da TSPLIB contendo 200
cidades.

O trecho em destaque da tabela e do gráfico da figura 65 possuem um comportamento


crescente a medida em que o número de tarefas diminui.

65
A seguir são apresentados os grafos correspondentes aos ciclos do resultado em desta-
que da figura 65:

Figura 66: Ciclo do resultado em destaque relativo a figura 65 com 2 tarefas.

Figura 67: Ciclo do resultado em destaque relativo a figura 65 com 4 tarefas.

66
Figura 68: Ciclo do resultado em destaque relativo a figura 65 com 8 tarefas.

Figura 69: Ciclo do resultado em destaque relativo a figura 65 com 16 tarefas.

67
Figura 70: Ciclo do resultado em destaque relativo a figura 65 com 32 tarefas.

É válido reforçar que os testes com o pipeline 3 apresentaram um comportamento


aderente aos testes dos pipelines 1 e 2.

Os resultados completos dos testes apresentados podem ser encontrados no endereço:


http://bit.ly/1gyT9HO

68
8 CONCLUSÃO E TRABALHOS FUTUROS

De acordo com os resultados apresentados no capı́tulo anterior, é possı́vel interpre-


tar que a utilização da solução composicional hı́brida em paralelo proposta nesta obra,
na maioria dos casos analisados, incrementará as possibilidades de exploração e seleção
de bons resultados dentro das diversas regiões do espaço de estados de problemas com
distribuições similares. Além disso, esta abordagem permite a resolução deste tipo de
problema dentro de um tempo de execução compatı́vel com os requisitos não-funcionais
dos problemas aplicados no mundo real.

Os objetivos da pesquisa proposta também foram alcançados com a análise, a im-


plementação, a investigação e os resultados obtidos através da proposição do modelo
composicional e a aplicação encadeada com paralelismo de algoritmos de aproximação,
randomização e heurı́sticas da Inteligência Artificial, selecionados exclusivamente para
explorar o Problema do Caixeiro Viajante (PCV).

Em termos de trabalhos futuros, os seguintes pontos poderão ser avançados:

• A extensão do modelo composicional para outros tipos de algoritmos que objetivam


a resolução do PCV. Bem como, a criação de uma maior diversidade ou variedade
de pipelines;

• A criação de funções transformadoras onde a execução poderá ocorrer em outros


tipos de processadores, como por exemplo GPUs, e/ou residir(em) em outra(s)
máquina(s), como por exemplo em Clusters ou em alguma infraestrutura de Cloud
Computing;

• Aplicar ferramentas estatı́sticas para formalizar e validar a solução proposta;

• Análise e ajustes finos dos argumentos das funções associados aos algoritmos me-
taheurı́sticos aplicados (SA, GA e ACO);

• Adaptação desta abordagem para outros problemas complexos, como por exemplo
o Problema da Mochila (PM) ou o Problema de Roteamento de Veı́culos (PRV).

69
REFERÊNCIAS BIBLIOGRÁFICAS

AKHTER, S. Multi-Core Programming. USA: Intel Press, 2006.

AMDAHL, G. M. Validity of the single processor approach to achieving large scale


computing capabilities. In: Proceedings of the April 18-20, 1967, Spring Joint Computer
Conference. New York, NY, USA: ACM, 1967. (AFIPS ’67 (Spring)), p. 483–485.
Disponı́vel em: <http://doi.acm.org/10.1145/1465482.1465560>.

APPLEGATE, D. L. The Traveling Salesman Problem: A Computational Study


(Princeton Series in Applied Mathematics). Princeton, NJ, USA: Princeton University
Press, 2007.

ASANOVIC, K. A view of the parallel computing landscape. Commun. ACM, ACM,


New York, NY, USA, v. 52, n. 10, p. 56–67, out. 2009. ISSN 0001-0782. Disponı́vel em:
<http://doi.acm.org/10.1145/1562764.1562783>.

AYDIN, M. E. Parallel simulated annealing. In: ALBA, E. (Ed.). Parallel Metaheuristics.


John Wiley and Sons Inc., 2005. cap. 12, p. 267–287. ISBN 9780471739388. Disponı́vel
em: <http://dx.doi.org/10.1002/0471739383.ch12>.

BABBAGE, C. Passages from The Life Of A Philosopher. London, UK: Longman,


Green, Longman, Roberts, & Green, 1864.

BELFIORE, P. Pesquisa Operacional para cursos de Engenharia. 1. ed. [S.l.]: Elsevier,


2013. 394–395 p.

BLUM, C. Ant colony optimization: Introduction and recent trends. Physics of


Life Reviews, v. 2, n. 4, p. 353 – 373, 2005. ISSN 1571-0645. Disponı́vel em:
<http://www.sciencedirect.com/science/article/pii/S1571064505000333>.

BURTSCHER, M. A scalable heterogeneous parallelization framework for iterative local


searches. 2013. 27th IEEE International Parallel and Distributed Processing Symposium.
Disponı́vel em: <http://hgpu.org/?p=9022>.

C++. ISO/IEC 14882: Programming Language C++. 2011. http://www.iso.org/iso/


iso_catalogue/catalogue_ics/catalogue_detail_ics.htm?csnumber=50372. Acesso
em: 01 de Junho de 2013.

70
CECILIA, J. Enhancing data parallelism for ant colony optimization on gpus. J. Parallel
Distrib. Comput., Academic Press, Inc., Orlando, FL, USA, v. 73, n. 1, p. 42–51, jan.
2013. ISSN 0743-7315. Disponı́vel em: <http://dx.doi.org/10.1016/j.jpdc.2012.01.002>.

CHIBANTE, R. Simulated annealing theory with applications. Scyio, ago. 2010.


Disponı́vel em: <http://doi.org/10.5772/252>.

COOK, W. J. In Pursuit of the Traveling Salesman: Mathematics at the Limits of


Computation). Princeton, NJ, USA: Princeton University Press, 2011.

CORMEN, T. H. Introduction to Algorithms. 3. ed. [S.l.]: The MIT Press, 2009.

CROES, G. A. A method for solving traveling-salesman problems. Operations Research,


v. 6, n. 6, p. 791–812, 1958. Disponı́vel em: <http://pubsonline.informs.org/doi/abs/10-
.1287/opre.6.6.791>.

CZECH, Z. J. A parallel simulated annealing algorithm as a tool for fitness landscapes


exploration. In: ROS, A. (Ed.). Parallel and Distributed Processing. 1. ed. [s.n.], 2010. p.
247–271. Disponı́vel em: <http://doi.org/10.5772/9455>.

DANTZIG, G. Solution of a large-scale traveling-salesman problem. Operations


Research, v. 2, p. 393–410, 1954. Disponı́vel em: <http://citeseer.ist.psu.edu/viewdoc-
/summary?doi=10.1.1.134.9319>.

DORIGO, M. The ant system: Optimization by a colony of cooperating agents. IEEE


TRANSACTIONS ON SYSTEMS, MAN, AND CYBERNETICS-PART B, v. 26, n. 1,
p. 29–41, 1996.

DORIGO, M. Ant colony system: A cooperative learning approach to the traveling


salesman problem. IEEE TRANSACTIONS ON EVOLUTIONARY COMPUTATION,
1997.

DORIGO, M. Ant Colony Optimization. Scituate, MA, USA: Bradford Company, 2004.
ISBN 0262042193.

DORIGO, M. Ant colony optimization. Computational Intelligence Magazine, IEEE,


v. 1, n. 4, p. 28–39, 2006. ISSN 1556-603X.

71
FERRUCCI, D. A. Introduction to ”this is watson”. IBM Journal of Research and
Development, v. 56, n. 3–4, p. 1:1–1:15, 2012. Acesso em: 01 de Maio de 2013.

FSHARP. The FSharp Software Foundation. 2013. http://fsharp.org/. Acesso em: 01


de Maio de 2013.

GAMMA, E. Design Patterns: Elements of Reusable Object-oriented Software. Boston,


MA, USA: Addison-Wesley Longman Publishing Co., Inc., 1995. ISBN 0-201-63361-2.

GAREY, M. R. Computers and Intractability: A Guide to the Theory of NP-


Completeness. New York, NY, USA: W. H. Freeman & Co., 1979. ISBN 0716710447.

GAREY, M. R. A list of np-complete problems. In: . New York, NY, USA: W. H.


Freeman & Co., 1979. cap. Appendix, p. 211–212. ISBN 0716710447.

GAZOLLA, J. G. F. M. Uma Abordagem HeurÃstica e Paralela em GPUs para o


Problema de Caixeiro Viajante. 2010. Acesso em: 2 de Setembro 2013. Disponı́vel em:
<http://www2.ic.uff.br/PosGraduacao/Dissertacoes/472.pdf>.

GOMEZ, J. Grisland: a parallel genetic algorithm for finding near optimal solutions
to the traveling salesman problem. In: Proceedings of the 11th Annual Conference
Companion on Genetic and Evolutionary Computation Conference: Late Breaking
Papers. New York, NY, USA: ACM, 2009. (GECCO ’09), p. 2035–2040. ISBN
978-1-60558-505-5. Disponı́vel em: <http://doi.acm.org/10.1145/1570256.1570272>.

HAREL, D. Algorithmics: The Spirit of Computing. 3. ed. USA: Springer Publishing


Company, Incorporated, 2012.

HAREL, D. Imperfect solutions to np-complete problems. In: . 3. ed. USA: Springer


Publishing Company, Incorporated, 2012. cap. 3, p. 203–204.

HASKELL.ORG. All About Monads. 2011. http://www.haskell.org/haskellwiki/


All_About_Monads#Introduction_2. Acesso em: 10 de Dezembro de 2013.

HOLLAND, J. H. Adaptation in Natural and Artificial Systems. Cambridge, MA, USA:


MIT Press, 1992. ISBN 0-262-58111-6.

72
HOLLAND, J. H. Genetic Algorithms: Computer programs that evolve in ways that
resemble natural selection can solve complex problems even their creators do not fully
understand. Scientific American, v. 267, p. 66–72, 1992.

HUGHES, J. Generalising monads to arrows. Science of Computer Programming, v. 37,


p. 67–111, 1998.

ISOCPP. ISO International Standard ISO/IEC 14882:2011(E) - Programming Language


C++. 2011. http://isocpp.org/std/the-standard. Acesso em: 20 de Novembro de
2013.

KIRKPATRICK, S. Optimization by simulated annealing. Science, v. 220, p. 671–680,


1983.

KIRKPATRICK, S. Neurocomputing: foundations of research. In: ANDERSON,


J. A.; ROSENFELD, E. (Ed.). Cambridge, MA, USA: MIT Press, 1988. cap.
Optimization by simulated annealing, p. 551–567. ISBN 0-262-01097-6. Disponı́vel em:
<http://dl.acm.org/citation.cfm?id=65669.104435>.

KLIMASAUSKAS, C. C. Not knowing your random number generator could be costly:


Random generators - why are they important. PC Artificial Intelligence Magazine, n. 16,
p. 52–58, 2002.

LUCCI, S. Ant colony optimization. In: LUCCI, S.; KOPEC, D. (Ed.). Artificial
Intelligence in the 21st Century. 1. ed. Dulles, VA, USA: Mercury Learning and
Information, 2012. p. 382–386. ISBN 1936420236, 9781936420230.

MANDELBROT, B. The (Mis)behavior of Markets: A Fractal View of Risk, Ruin, and


Reward. 1. ed. New York, NY, USA: Basic Books, 2004. 186–195 p. ISBN 0465043550.

MATTSON, T. G. Patterns for Parallel Programming. 1. ed. Boston, MA, USA:


Addison-Wesley Professional, 2004. ISBN 0321228111.

MENDES, R. O estranho caso do vendedor ambulante. Revista Cálculo, Editora


Segmento, São Paulo, SP, BRAZIL, v. 2, n. 22, p. 48–53, out. 2012.

MICHALEWICZ, Z. How to Solve It: Modern Heuristics. 2. ed. Berlin, Heidelberg:


Springer-Verlag, 2010. 192–214 p. ISBN 3642061346, 9783642061349.

73
MICROSOFT. Computation Expressions in FSharp. 2013. http://msdn.microsoft.
com/en-us/library/dd233182.aspx. Acesso em: 10 de Dezembro de 2013.

MICROSOFT. Enumerable.SelectMany. 2013. http://msdn.microsoft.com/en-us/


library/bb534336.aspx. Acesso em: 10 de Dezembro de 2013.

MITCHELL, M. Genetic algorithms. In: Encyclopedia of Computer Science. Chichester,


UK: John Wiley and Sons Ltd., 2003. p. 747–748. ISBN 0-470-86412-5. Disponı́vel em:
<http://dl.acm.org/citation.cfm?id=1074100.1074423>.

NEUMANN, J. von. First draft of a report on the edvac. IEEE Ann. Hist. Comput.,
IEEE Educational Activities Department, Piscataway, NJ, USA, v. 15, n. 4, p. 27–75,
out. 1993. ISSN 1058-6180. Disponı́vel em: <http://dx.doi.org/10.1109/85.238389>.

O’NEIL, M. A. A parallel gpu version of the traveling salesman problem. In:


International Conference on Parallel and Distributed Processing Techniques and
Applications (PDPTA’11). [s.n.], 2011. p. 348–353. Disponı́vel em: <http://hgpu.org-
/?p=6604>.

OPENMP. OpenMP Application Program Interface 3.0. 2008. http://www.openmp.org/


mp-documents/spec30.pdf. Acesso em: 03 de Novembro de 2013.

PARLAB@BERKELEY. A Pattern Language for Parallel Programming. 2010.


http://parlab.eecs.berkeley.edu/wiki/patterns/patterns. Acesso em: 18 de
Maio de 2013.

RAJAN, H. Concurrency by modularity: Design patterns, a case in point. In:


Proceedings of the ACM International Conference on Object Oriented Programming
Systems Languages and Applications. New York, NY, USA: ACM, 2010. (OOPSLA
’10), p. 790–805. ISBN 978-1-4503-0203-6. Disponı́vel em: <http://doi.acm.org/10.1145-
/1869459.1869523>.

REINELT, G. TSPLIB. 1991. http://comopt.ifi.uni-heidelberg.de/software/


TSPLIB95/. Acesso em: 05 de Junho de 2013.

REINELT, G. Tsplib - a traveling salesman problem library. ORSA Journal on


Computing, v. 3, n. 4, p. 376–384, 1991. Disponı́vel em: <http://pubsonline.informs.org-
/doi/abs/10.1287/ijoc.3.4.376>.

74
ROBERT, C. P. Introducing Monte Carlo Methods with R (Use R). 1. ed. Berlin,
Heidelberg, Germany: Springer-Verlag, 2009. 167–197 p. ISBN 1441915753,
9781441915757.

ROCKI, K. Accelerating 2-opt and 3-opt local search using gpu in the travelling salesman
problem. In: Proceedings of the 2012 12th IEEE/ACM International Symposium on
Cluster, Cloud and Grid Computing (ccgrid 2012). Washington, DC, USA: IEEE
Computer Society, 2012. (CCGRID ’12), p. 705–706. ISBN 978-0-7695-4691-9. Disponı́vel
em: <http://dx.doi.org/10.1109/CCGrid.2012.133>.

RUSSELL, S. Artificial Intelligence: A Modern Approach. 3. ed. Upper Saddle River,


NJ, USA: Prentice Hall Press, 2009. 122–125 p.

SCALA-LANG.ORG. scala.collection.generic.FilterMonadic trait. 2013. http:


//www.scala-lang.org/api/current/#scala.collection.generic.FilterMonadic.
Acesso em: 10 de Dezembro de 2013.

SEDGEWICK, R. Algorithms. 4. ed. [S.l.]: Addison-Wesley, 2009.

SKISCIM, C. C. Optimization by simulated annealing: A preliminary computational


study for the tsp. In: Proceedings of the 15th Conference on Winter Simulation - Volume
2. Piscataway, NJ, USA: IEEE Press, 1983. (WSC ’83), p. 523–535. Disponı́vel em:
<http://dl.acm.org/citation.cfm?id=800044.801546>.

SUTTER, H. Break Amdahl’s Law! jan. 2008. http://www.drdobbs.com/parallel/


break-amdahls-law/205900309. Acesso em: 01 de Maio de 2013.

TOSIC, P. T. A perspective on the future of massively parallel computing: fine-grain vs.


coarse-grain parallel models comparison & contrast. In: Proceedings of the 1st conference
on Computing frontiers. New York, NY, USA: ACM, 2004. (CF ’04), p. 488–502. ISBN
1-58113-741-9. Disponı́vel em: <http://doi.acm.org/10.1145/977091.977160>.

TSP@GATECH. Sweden CPU Time. 2004. http://www.math.uwaterloo.ca/tsp/


sweden/compute/cpu.htm. Acesso em: 07 de Dezembro de 2013.

TSP@GATECH. Progress in Solving TSPs. 2005. http://www.tsp.gatech.edu/


methods/progress/progress.htm. Acesso em: 01 de Maio de 2013.

75
TSP@GATECH. Optimal Tours. 2007. http://www.tsp.gatech.edu/optimal/index.
html. Acesso em: 01 de Maio de 2013.

TSP@GATECH. TSP Test Data. 2009. http://www.tsp.gatech.edu/data/index.


html. Acesso em: 05 de Junho de 2013.

VOSS, S. Meta-heuristics: The state of the art. In: LOCAL SEARCH FOR PLANNING
AND SCHEDULING. [s.n.], 2006. Acesso em: 18 de Novembro 2013. Disponı́vel em:
<http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.203.4989>.

WADLER, P. Comprehending monads. In: Mathematical Structures in Computer


Science. [S.l.: s.n.], 1992. p. 61–78.

WADLER, P. Monads for functional programming. 1995.

76
ANEXOS

Nesta seção são listados os anexos do trabalho. Através deles foram gerados as si-
mulações e obtidos os resultados apresentados.

Basicamente, eles são os códigos fontes contendo o modelo proposto desta obra com
seus respectivos artefatos. O código fonte é apresentado em C++ 11 em forma de header
files (.hpp) ou source files (.cpp). Abaixo segue a lista de arquivos anexados:

• tsp.hpp

• randomizers.hpp

• ant colony optimization.hpp

• genetic algorithm.hpp

• nearest neighbour.hpp

• simulated annealing.hpp

• two opt.hpp

• tsp monad.hpp

• tsp functors.hpp

• tsp algo.hpp

• tsp support.hpp

• tsp.hpp

• program.cpp

Os arquivos acima também são encontrados no endereço: http://bit.ly/1cL3gpG

77
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\src\tsp.hpp 1
1 //Sample provided by Fabio Galuppo
2 //Compositional TSP Solver version 0.1
3 //October 2013
4
5 #pragma once
6 #ifndef _tsp_class_
7 #define _tsp_class_
8
9 #include <vector>
10 #include <string>
11
12 #include <cmath>
13 #include <functional>
14
15 struct tsp_class
16 {
17 typedef std::pair<int /* x */, int /* y */> city_location;
18 typedef std::function<int()> rand_function;
19
20 struct city_info
21 {
22 int id;
23 city_location location;
24 };
25
26 typedef std::vector<city_info> cities_type;
27
28 typedef std::wstring description_type;
29
30 cities_type cities;
31 description_type description;
32
33 //Semiregular:
34 tsp_class()
35 {
36 }
37
38 tsp_class(const tsp_class& that)
39 {
40 cities = that.cities;
41 }
42
43 tsp_class& operator=(const tsp_class& that)
44 {
45 if (this != &that)
46 cities = that.cities;
47 return *this;
48 }
49
50 ~tsp_class()
51 {
52 }
53
54 //Regular:
55 friend bool operator==(const tsp_class& lhs, const tsp_class& rhs) 
56 {
57 if (lhs.cities.size() == rhs.cities.size())
58 {
59 auto x = lhs.cities.cbegin();
60 auto y = rhs.cities.cbegin();
61
62 while(x != lhs.cities.cend() || y != rhs.cities.cend())
63 {
64 bool a = x­>id != y­>id,
65 b = x­>location.first != y­>location.first,
66 c = x­>location.second != y­>location.second;
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\src\tsp.hpp 2
67
68 if (a || b || c) 
69 return false;
70
71 ++x; 
72 ++y;
73 }
74
75 return true;
76 }
77
78 return false;
79 }
80
81 friend bool operator!=(const tsp_class& lhs, const tsp_class& rhs)
82 {
83 return !(lhs == rhs);
84 }
85
86 //
87 double do_cycle_length() const
88 {
89 cities_type::size_type N = cities.size();
90
91 double temp = euclidean_distance(cities[N ­ 1].location, cities[0].location);   
92
93 for (int i = 0, l = N ­ 1; i < l; ++i)
94 temp += euclidean_distance(cities[i].location, cities[i + 1].location);
95
96 return temp;
97 }
98
99 //perturb_first: false means don't perturb starting/ending city
100 void do_pertubation(rand_function rnd_city, bool perturb_first)
101 {
102 int c1, c2;
103 do
104 {
105 c1 = rnd_city();
106 do c2 = rnd_city(); while(c1 == c2);
107 } while(!perturb_first && (0 == c1 || 0 == c2));
108
109 std::swap(cities[c1], cities[c2]);
110 }
111
112 static double euclidean_distance(city_location c1, city_location c2)
113 {
114 auto x1 = c1.first;
115 auto y1 = c1.second;
116 auto x2 = c2.first;
117 auto y2 = c2.second;
118 auto dx = std::abs(x1 ­ x2);
119 auto dy = std::abs(y1 ­ y2);
120 return std::sqrt(static_cast<double>(dx * dx + dy * dy));
121 }
122 };
123
124 #include <iostream>
125
126 void mathematica_graph_plot(tsp_class& tsp_instance, bool vertexLabeling = false, bool plotLabel = 
true)
127 {
128 auto LEN = tsp_instance.cities.size() ­ 1;
129
130 std::cout << "GraphPlot[{";
131 for (int i = 0, l = LEN; i < l; ++i)
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\src\tsp.hpp 3
132 std::cout << i << " ­> " << i + 1 << ", ";
133 std::cout << LEN <<  " ­> " << 0 << "}, ";
134
135 if (plotLabel) 
136 std::cout << "PlotLabel ­> " << "\"" << tsp_instance.do_cycle_length() <<  "\", ";
137
138 std::cout << "Frame ­> True, ";
139
140 std::cout << "VertexLabeling ­> " <<  (vertexLabeling ? "True" : "False") <<  ", ";
141
142 std::cout << "VertexCoordinateRules ­> {";
143 for (int i = 0, l = LEN; i < l; ++i)
144 {
145 auto& city = tsp_instance.cities[i];
146 std::cout << i << " ­> {" << city.location.first << ", " << city.location.second << "}, ";
147 }
148 auto& city = tsp_instance.cities[LEN];
149 std::cout << LEN << " ­> {" << city.location.first << ", " << city.location.second << "}}]" << std:
:endl;
150 }
151
152 void display(tsp_class& tsp_instance, bool emit_mathematica_graph_plot = false)
153 {
154 for(auto city = tsp_instance.cities.cbegin(); city != tsp_instance.cities.cend(); ++city)
155 std::cout << "[" << city­>id << "]" << "(" << city­>location.first << ", " << city­>location.
second << ") : ";
156 std::cout << tsp_instance.do_cycle_length() << std::endl;
157
158 if(emit_mathematica_graph_plot)
159 mathematica_graph_plot(tsp_instance);
160 }
161
162 #endif /* _tsp_class_ */
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\TSPSolution\src\randomizers.hpp 1
1 //Sample provided by Fabio Galuppo
2 //Compositional TSP Solver version 0.1
3 //October 2013
4
5 #pragma once
6 #ifndef _randomizers_
7 #define _randomizers_
8
9 #include <memory>
10 #include <random>
11 #include <ctime>
12
13 struct random_functor
14 {
15 random_functor(int min_inclusive, int max_inclusive, unsigned seed = static_cast<unsigned>(std::time
(nullptr))) :
16 Engine_(new std::default_random_engine(seed)),
17 Rnd_(new std::uniform_int_distribution<int>(min_inclusive, max_inclusive))
18 {
19 }
20
21 random_functor(int max_exclusive, unsigned seed = static_cast<unsigned>(std::time(nullptr))) :
22 Engine_(new std::default_random_engine(seed)),
23 Rnd_(new std::uniform_int_distribution<int>(0, max_exclusive ­ 1))
24 {
25 }
26
27 //[0; max_inclusive]
28 auto operator()() const ­> int { return (*Rnd_)(*Engine_); }
29
30 private:
31 std::shared_ptr<std::default_random_engine> Engine_;
32 std::shared_ptr<std::uniform_int_distribution<int>> Rnd_;
33 };
34
35 struct double_random_functor
36 {
37 double_random_functor(unsigned seed = static_cast<unsigned>(std::time(nullptr))) :
38 Engine_(new std::default_random_engine(seed)),
39 Rnd_(new std::uniform_real_distribution<double>())
40 {
41 }
42
43 //[0.0; 1.0]
44 auto operator()() const ­> double { return (*Rnd_)(*Engine_); }
45
46 private:
47 std::shared_ptr<std::default_random_engine> Engine_;
48 std::shared_ptr<std::uniform_real_distribution<double>> Rnd_;
49 };
50
51 #endif /* _randomizers_ */
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\src\algorithms\ant_colony_optimization.hpp 1
1 //Sample provided by Fabio Galuppo
2 //Compositional TSP Solver version 0.1
3 //October 2013
4
5 #pragma once
6 #ifndef _ant_colony_optimization_
7 #define _ant_colony_optimization_
8
9 #include "..\tsp.hpp"
10 #include "..\randomizers.hpp"
11
12 #include <cmath>
13 #include <memory>
14 #include <algorithm>
15 #include <utility>
16 #include <limits>
17
18 typedef std::vector<std::vector<double>> pheromones_type;
19
20 struct ant_class;
21
22 typedef std::vector<std::shared_ptr<ant_class>> ants_type;
23
24 struct ant_class
25 {
26 typedef std::vector<bool> tabu_type;
27 typedef std::vector<unsigned short> tour_type;
28
29 ant_class(int size, int city,
30 const pheromones_type& pheromones,
31 const tsp_class& tsp_instance,
32 std::unique_ptr<double_random_functor> random_func = std::unique_ptr<double_random_functor>(new
double_random_functor())) :
33 referential_city(city),
34 pheromones(pheromones),
35 tsp_instance(tsp_instance),
36 random_func(std::move(random_func))
37 {
38 tabu.resize(size);
39 tour.resize(size);
40 reset();
41 }
42
43 void reset()
44 {
45 current_city = referential_city;
46
47 tabu_type::size_type N = tabu.size();
48 for (tabu_type::size_type i = 0; i < N; ++i)
49 {
50 tabu[i] = false;
51 tour[i] = 0;
52 }
53
54 tabu[current_city] = true;
55 tour[0] = current_city;
56 tour_index = 1;
57 tour_length = 0.0;
58 }
59
60 tour_type tour;
61
62 private:
63 tabu_type tabu;
64
65 int current_city;
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\src\algorithms\ant_colony_optimization.hpp 2
66 int next_city;
67 tour_type::size_type tour_index;
68 double tour_length;
69 const pheromones_type& pheromones;
70 const tsp_class& tsp_instance;
71 std::unique_ptr<double_random_functor> random_func;
72
73 bool move(const double alpha /* favor pheromone level over distance */, const double beta /* favor
distance over pheromone level */)
74 {
75 tour_type::size_type N = tour.size();
76 if (tour_index < N)
77 {
78 take_next_city(alpha, beta);
79 return true;
80 }
81
82 return false;
83 }
84
85 void take_next_city(const double alpha /* favor pheromone level over distance */, const double beta
/* favor distance over pheromone level */)
86 {
87 double total_for_non_visited_cities = 0.0;
88 tsp_class::cities_type::size_type from = current_city, to = 0;
89 for(auto i = tabu.cbegin(); i != tabu.cend(); ++i, ++to)
90 if (*i == 0)
91 total_for_non_visited_cities += product_of_pheronome_level_and_distance(tsp_instance,
pheromones, from, to, alpha, beta);
92
93 to = 0;
94 tabu_type::size_type N = tabu.size();
95 unsigned short max_iteration_guard = std::numeric_limits<unsigned short>::max();
96 while (max_iteration_guard > 0)
97 {
98 if (!tabu[to])
99 {
100 const double current_product_of_pheronome_level_and_distance =
product_of_pheronome_level_and_distance(tsp_instance, pheromones, from, to, alpha, beta);
101 const double visitProbability = current_product_of_pheronome_level_and_distance /
total_for_non_visited_cities;
102
103 if ((*random_func)() < visitProbability)
104 break;
105 else
106 max_iteration_guard--;
107 }
108
109 to = (to + 1) % N;
110 }
111
112 next_city = to;
113 tabu[next_city] = true;
114 tour[tour_index++] = next_city;
115
116 tour_length += tsp_class::euclidean_distance(tsp_instance.cities[current_city].location,
tsp_instance.cities[next_city].location);
117 if (tour_index == N)
118 tour_length += tsp_class::euclidean_distance(tsp_instance.cities[N-1].location,
tsp_instance.cities[0].location);
119
120 current_city = next_city;
121 }
122
123 static double product_of_pheronome_level_and_distance(const tsp_class& tsp_instance, const
pheromones_type& pheromones, pheromones_type::size_type from, pheromones_type::size_type to, double
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\src\algorithms\ant_colony_optimization.hpp 3
alpha, double beta)
124 {
125 const auto level_of_pheronome_between_current_and_candidate_location = std::pow(pheromones
[from][to], alpha);
126 const auto distance_between_current_and_candidate_location = std::pow((1.0 / tsp_class::
euclidean_distance(tsp_instance.cities[from].location, tsp_instance.cities[to].location)), beta);
127 return level_of_pheronome_between_current_and_candidate_location *
distance_between_current_and_candidate_location;
128 }
129
130 friend static void update_pheromone_trails(const tsp_class&, pheromones_type&, const ants_type&,
const double, const double, const double);
131 friend static void take_min_cycle(tsp_class&, const ants_type&);
132 friend static void ant_colony_optimization(tsp_class&, double, unsigned int, const ants_type::
size_type, const double, const double, const double, const double, const unsigned int);
133
134 private:
135 unsigned short referential_city;
136 };
137
138 void update_pheromone_trails(const tsp_class& tsp_instance, pheromones_type& pheromones, const
ants_type& ants, const double base_pheromone, const double rho, const double qval)
139 {
140 const tsp_class::cities_type::size_type N = tsp_instance.cities.size();
141
142 //evaporate
143 for (tsp_class::cities_type::size_type i = 0; i < N; ++i)
144 {
145 for (tsp_class::cities_type::size_type j = 0; j < N; ++j)
146 {
147 pheromones[i][j] *= (1.0 - rho);
148 if (pheromones[i][j] < 0.0)
149 pheromones[i][j] = base_pheromone;
150 }
151 }
152
153 const ants_type::size_type A = ants.size();
154
155 //intensify
156 for(auto ant = ants.cbegin(); ant != ants.end(); ++ant)
157 {
158 for (tsp_class::cities_type::size_type i = 0; i < N; ++i)
159 {
160 int from = (*ant)->tour[i];
161 int to = (*ant)->tour[((i+1) % N)];
162
163 pheromones[from][to] += ((qval / (*ant)->tour_length) * rho);
164 pheromones[to][from] = pheromones[from][to];
165 }
166 }
167 }
168
169 void take_min_cycle(tsp_class& tsp_instance, const ants_type& ants)
170 {
171 std::vector<std::pair<int, double>> tours_lenght;
172 tours_lenght.resize(ants.size());
173
174 int i = 0;
175 std::transform(ants.cbegin(), ants.cend(), tours_lenght.begin(), [&i](const std::shared_ptr
<ant_class>& a)
176 {
177 return std::make_pair(i++, (*a).tour_length);
178 });
179
180 std::sort(tours_lenght.begin(), tours_lenght.end(), [](const std::pair<int, double>& lhs, const std
::pair<int, double>& rhs)
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\src\algorithms\ant_colony_optimization.hpp 4
181 {
182 return lhs.second < rhs.second;
183 });
184
185 auto best_tour = *tours_lenght.begin();
186 ant_class& best_ant = *(ants[best_tour.first].get());
187
188 tsp_class temp = tsp_instance;
189 i = 0;
190 for(auto iter = best_ant.tour.cbegin(); iter != best_ant.tour.cend(); ++iter)
191 temp.cities[i++] = tsp_instance.cities[*iter];
192
193 if (temp.do_cycle_length() <= tsp_instance.do_cycle_length())
194 tsp_instance = temp;
195 }
196
197 void ant_colony_optimization(tsp_class& tsp_instance,
198 const double base_pheromone,
199 unsigned int iterations,
200 const ants_type::size_type number_of_ants,
201 const double favor_pheromone_level_over_distance,
202 const double favor_distance_over_pheromone_level,
203 const double value_for_intensification_and_evaporation,
204 const double pheronome_distribution,
205 const unsigned int rnd_seed)
206 {
207 const auto N = tsp_instance.cities.size();
208 random_functor rnd_Q(static_cast<int>(pheronome_distribution), 100, rnd_seed);
209 double Q = rnd_Q();
210
211 pheromones_type pheromones;
212 ants_type ants;
213 {
214 random_functor delay(1, number_of_ants * 2);
215 std::vector<std::unique_ptr<double_random_functor>> double_random_functors;
216 for (ants_type::size_type i = 0; i < number_of_ants; ++i)
217 double_random_functors.push_back(std::unique_ptr<double_random_functor>(new
double_random_functor(rnd_seed + i * delay())));
218
219 for (std::vector<std::shared_ptr<ant_class>>::size_type a = 0; a < number_of_ants; ++a)
220 ants.push_back(std::shared_ptr<ant_class>(new ant_class(N, a % N, pheromones, tsp_instance,
std::move(double_random_functors[a]))));
221 }
222
223 pheromones.reserve(N * N);
224 for (tsp_class::cities_type::size_type i = 0; i < N; ++i)
225 {
226 std::vector<double> v;
227 for (tsp_class::cities_type::size_type j = 0; j < N; ++j)
228 v.push_back(base_pheromone);
229 pheromones.push_back(v);
230 }
231
232 while (iterations-- > 0)
233 {
234 int moved = 0;
235 for (auto a = ants.begin(); a != ants.end(); ++a)
236 moved += (*a)->move(favor_pheromone_level_over_distance,
favor_distance_over_pheromone_level) ? 1 : 0;
237 if (0 == moved)
238 {
239 //update_pheromone_trails(tsp_instance, pheromones, ants, base_pheromone,
value_for_intensification_and_evaporation, pheronome_distribution);
240 update_pheromone_trails(tsp_instance, pheromones, ants, base_pheromone,
value_for_intensification_and_evaporation, Q);
241
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\src\algorithms\ant_colony_optimization.hpp 5
242 if (iterations > 0)
243 {
244 take_min_cycle(tsp_instance, ants);
245 for (auto a = ants.begin(); a != ants.end(); ++a)
246 (*a)->reset();
247 }
248 }
249 }
250 }
251
252 #endif /* _ant_colony_optimization_ */
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\src\algorithms\genetic_algorithm.hpp 1
1 //Sample provided by Fabio Galuppo
2 //Compositional TSP Solver version 0.1
3 //October 2013
4
5 #pragma once
6 #ifndef _genetic_algorithm_
7 #define _genetic_algorithm_
8
9 #include "..\tsp.hpp"
10 #include "..\randomizers.hpp"
11
12 #include <iostream>
13 #include <utility>
14 #include <vector>
15 #include <algorithm>
16 #include <functional>
17 #include <limits>
18
19 typedef std::pair<typename tsp_class::cities_type::size_type /* id */, double /* distance */> 
distance_value_type;
20 typedef std::vector<distance_value_type> distance_collection_type;
21
22 struct tour
23 {
24 typedef std::pair<unsigned int, unsigned int> connection;
25 typedef std::vector<connection> connections_type;
26
27 connections_type connections;
28
29 tour(const tsp_class& tsp_instance) : 
30 Fitness_(0.0), 
31 TspInstance_(tsp_instance)
32 {
33 }
34
35 tour(const tour& that) : 
36 TspInstance_(that.TspInstance_)
37 {
38 connections = that.connections;
39 Fitness_ = do_fitness();
40 }
41
42 tour& operator=(const tour& that)
43 {
44 if (this != &that)
45 {
46 connections = that.connections;
47 const_cast<tsp_class&>(TspInstance_) = that.TspInstance_;
48 Fitness_ = do_fitness();
49 }
50 return *this;
51 }
52
53 ~tour()
54 {
55 }
56
57 void resize_and_reset()
58 {
59 connections.resize(TspInstance_.cities.size());
60 for (auto i = connections.begin(); i != connections.end(); ++i)
61 {
62 *i = std::make_pair(std::numeric_limits<unsigned int>::max(), 
63 std::numeric_limits<unsigned int>::max());
64 }
65 }
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\src\algorithms\genetic_algorithm.hpp 2
66
67 const std::pair<bool, bool> has_connection(connections_type::size_type index) const
68 {
69 const auto& i = connections[index];
70 return std::make_pair(i.first != no_connection(), 
71 i.second != no_connection());
72 }
73
74 double do_fitness()
75 {
76 double fitness = 0.0;
77 unsigned int previous_city = 0, next_city = connections[0].first;
78
79 for (auto i = connections.begin(); i != connections.end(); ++i)
80 {
81 fitness += tsp_class::euclidean_distance(TspInstance_.cities[previous_city].location, 
82 TspInstance_.cities[next_city].location);
83
84 bool x = previous_city != connections[next_city].first;
85 previous_city = next_city;
86 next_city =  x ? connections[next_city].first : connections[next_city].second;
87 }
88
89 Fitness_ = fitness;
90
91 return fitness;
92 }
93
94 const double fitness() const { return Fitness_; }
95
96 static unsigned int no_connection()
97 {
98 return std::numeric_limits<unsigned int>::max();
99 }
100
101 private:
102 double Fitness_;
103 const tsp_class& TspInstance_;
104 };
105
106 std::vector<distance_collection_type> 
107 make_closest_cities_table(const tsp_class& tsp_instance, 
108 distance_collection_type::size_type closest_cities_maxcount)
109 {
110 std::vector<distance_collection_type> distance_table;
111
112 tsp_class::cities_type::size_type i_idx = 0;
113 for (auto i = tsp_instance.cities.cbegin(); i != tsp_instance.cities.cend(); ++i, ++i_idx)
114 {
115 distance_collection_type distances;
116 distances.reserve(tsp_instance.cities.size() ­ 1 /* (*) */);
117
118 tsp_class::cities_type::size_type j_idx = 0;
119 for (auto j = tsp_instance.cities.cbegin(); j != tsp_instance.cities.cend(); ++j, ++j_idx)
120 {
121 if (j_idx != i_idx) //excluding self (*)
122 {
123 auto distance = tsp_class::euclidean_distance(i­>location, j­>location);
124 distances.push_back(std::make_pair(j_idx, distance));
125 }
126 }
127
128 std::sort(distances.begin(), distances.end(), [] (const distance_value_type& lhs, const 
distance_value_type& rhs)
129 {
130 return lhs.second < rhs.second;
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\src\algorithms\genetic_algorithm.hpp 3
131 });
132
133 if (distances.size() > closest_cities_maxcount) distances.resize(closest_cities_maxcount);
134
135 distance_table.push_back(std::move(distances));
136 }
137
138 return distance_table;
139 }
140
141 unsigned int& first_ref(const tour& t, tsp_class::cities_type::size_type index)
142 {
143 return const_cast<unsigned int&>(t.connections[index].first);
144 }
145
146 unsigned int& second_ref(const tour& t, tsp_class::cities_type::size_type index)
147 {
148 return const_cast<unsigned int&>(t.connections[index].second);
149 }
150
151 tsp_class tour_to_tsp_class(const tsp_class& tsp_instance, const tour& t)
152 {
153 tsp_class result;
154 result.cities.reserve(tsp_instance.cities.size());
155
156 unsigned int previous_city = 0, next_city = first_ref(t, 0);
157
158 for (tour::connections_type::size_type i = 0, l = t.connections.size(); i < l; ++i)
159 {
160 result.cities.push_back(tsp_instance.cities[next_city]);   
161 bool x = previous_city != first_ref(t, next_city);
162 previous_city = next_city;
163 next_city = x ? first_ref(t, next_city) : second_ref(t, next_city);
164 }
165
166 return result;
167 }
168
169 typedef std::vector<tour> population_type;
170
171 population_type make_population_via_selection(const tsp_class& tsp_instance, 
172 tour& best_tour, 
173 unsigned int population_size, 
174 std::vector<distance_collection_type> 
closest_cities_table, 
175 double closest_city_probability, 
176 tsp_class::rand_function rnd_tsp, 
177 std::function<double()> rnd_probability, 
178 tsp_class::rand_function rnd_closest_city)
179 {
180 const auto N = tsp_instance.cities.size();
181
182 population_type population;
183 population.reserve(population_size);
184
185 int begin_city, end_city;
186 for (unsigned int i = 0; i < population_size; ++i)
187 {
188 tour new_tour(tsp_instance);
189 new_tour.resize_and_reset();
190
191 begin_city = rnd_tsp();
192 end_city = begin_city;
193
194 int next_city;
195 for (unsigned int j = 0; j < N ­ 1; ++j)
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\src\algorithms\genetic_algorithm.hpp 4
196 {
197 do  next_city = (rnd_probability() < closest_city_probability) && (closest_cities_table[j].
size() > 0) ? 
198 closest_cities_table[j][rnd_closest_city()].first : rnd_tsp();
199 while (new_tour.has_connection(next_city).second || (next_city == end_city));
200
201 second_ref(new_tour, end_city) = next_city;
202 first_ref(new_tour, next_city) = end_city;
203 end_city = next_city;
204 second_ref(new_tour, end_city) = begin_city;
205 first_ref(new_tour, begin_city) = end_city;
206 }
207
208 //new_tour will execute do_fitness() in copy constructor when it's copied to population
209 population.push_back(new_tour);
210 }
211
212 best_tour = *std::min_element(population.begin(), population.end(), [](const tour& lhs, const tour&
rhs) {
213 return lhs.fitness() < rhs.fitness();
214 });
215
216 return population;
217 }
218
219 bool has_valid_connection(const tsp_class& tsp_instance, 
220 tour& t, 
221 std::vector<tsp_class::cities_type::size_type>& city_usage, 
222 tsp_class::cities_type::size_type first_city, 
223 tsp_class::cities_type::size_type second_city)
224 {
225
226 if ((first_city == second_city) || (2 == city_usage[first_city]) || (2 == city_usage[second_city]))
227 return false;
228
229 if ((0 == city_usage[first_city]) || (0 == city_usage[second_city]))
230 return true;
231
232 for (unsigned int i = 0; i < 2; ++i)
233 {
234 tsp_class::cities_type::size_type last_city = first_city;
235 auto connection = i == 0 ? first_ref(t, first_city) : second_ref(t, first_city);
236
237 tsp_class::cities_type::size_type tour_count = 0;
238 while ((connection != tour::no_connection()) && (connection != second_city) && (tour_count < 
tsp_instance.cities.size() ­ 2))
239 {
240 ++tour_count;
241
242 if (last_city != first_ref(t, connection))
243 {
244 last_city = connection;
245 connection = first_ref(t, connection);
246 }
247 else
248 {
249 last_city = connection;
250 connection = second_ref(t, connection);
251 }
252 }
253
254 if (tour_count >= tsp_instance.cities.size() ­ 2)
255 return true;
256
257 if (connection == second_city)
258 return false;
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\src\algorithms\genetic_algorithm.hpp 5
259 }
260
261 return true;
262 }
263
264 void connect_cities(tour& t, 
265 std::vector<tsp_class::cities_type::size_type>& city_usage, 
266 tsp_class::cities_type::size_type first_city, 
267 tsp_class::cities_type::size_type second_city)
268 {
269 if (t.connections[first_city].first == tour::no_connection())
270 t.connections[first_city].first = second_city;
271 else
272 t.connections[first_city].second = second_city;
273
274 if (t.connections[second_city].first == tour::no_connection())
275 t.connections[second_city].first = first_city;
276 else
277 t.connections[second_city].second = first_city;
278
279 ++city_usage[first_city];
280 ++city_usage[second_city];
281 }
282
283 unsigned int find_next_city(const tsp_class& tsp_instance, 
284 tour& parent, 
285 tour& child, 
286 std::vector<tsp_class::cities_type::size_type>& city_usage, 
287 int city)
288 {
289 if (has_valid_connection(tsp_instance, child, city_usage, city, first_ref(parent, city)))
290 return first_ref(parent, city);
291
292 if (has_valid_connection(tsp_instance, child, city_usage, city, second_ref(parent, city)))
293 return second_ref(parent, city);
294
295 return tour::no_connection();
296 }
297
298 tour crossover(const tsp_class& tsp_instance, 
299 tour& first_parent, 
300 tour& second_parent, 
301 const random_functor& rnd)
302 {
303 tour child(tsp_instance);
304 child.resize_and_reset();
305
306 std::vector<tsp_class::cities_type::size_type> city_usage;
307 city_usage.resize(tsp_instance.cities.size());
308 std::fill(city_usage.begin(), city_usage.end(), 0);
309
310 tsp_class::cities_type::size_type next_city = 0;
311 for (tsp_class::cities_type::size_type city = 0; city != tsp_instance.cities.size(); ++city)
312 {
313 if (city_usage[city] < 2)
314 {
315 auto& a = first_ref(first_parent, city);
316 auto& b = second_ref(first_parent, city);
317 auto& c = first_ref(second_parent, city);
318 auto& d = second_ref(second_parent, city);
319
320 if (a == c)
321 {
322 next_city = a;
323 if (has_valid_connection(tsp_instance, child, city_usage, city, next_city))
324 connect_cities(child, city_usage, city, next_city);
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\src\algorithms\genetic_algorithm.hpp 6
325 }
326
327 if (b == d)
328 {
329 next_city = b;
330 if (has_valid_connection(tsp_instance, child, city_usage, city, next_city))
331 connect_cities(child, city_usage, city, next_city);
332
333 }
334
335 if (a == d)
336 {
337 next_city = a;
338 if (has_valid_connection(tsp_instance, child, city_usage, city, next_city))
339 connect_cities(child, city_usage, city, next_city);
340 }
341
342 if (b == c)
343 {
344 next_city = b;
345 if (has_valid_connection(tsp_instance, child, city_usage, city, next_city))
346 connect_cities(child, city_usage, city, next_city);
347 }
348 }
349 }
350
351 for (tsp_class::cities_type::size_type city = 0; city != tsp_instance.cities.size(); ++city)
352 {
353 if (city_usage[city] < 2)
354 {
355 if (city % 2 == 1)
356 {
357 next_city = find_next_city(tsp_instance, first_parent, child, city_usage, city);
358 if (next_city == tour::no_connection())
359 next_city = find_next_city(tsp_instance, second_parent, child, city_usage, city);
360 }
361 else
362 {
363 next_city = find_next_city(tsp_instance, second_parent, child, city_usage, city);
364 if (next_city == tour::no_connection())
365 next_city = find_next_city(tsp_instance, first_parent, child, city_usage, city);
366 }
367
368 if (next_city != tour::no_connection())
369 {
370 connect_cities(child, city_usage, city, next_city);
371
372 if (city_usage[city] == 1)
373 {
374 if (city % 2 != 1)
375 {
376 next_city = find_next_city(tsp_instance, first_parent, child, city_usage, city)
;
377 if (next_city == tour::no_connection())
378 next_city = find_next_city(tsp_instance, second_parent, child, city_usage, 
city);
379 }
380 else
381 {
382 next_city = find_next_city(tsp_instance, second_parent, child, city_usage, 
city);
383 if (next_city == tour::no_connection())
384 next_city = find_next_city(tsp_instance, first_parent, child, city_usage, 
city);
385 }
386
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\src\algorithms\genetic_algorithm.hpp 7
387 if (next_city != tour::no_connection())
388 connect_cities(child, city_usage, city, next_city);
389 }
390 }
391 }
392 }
393
394 for (tsp_class::cities_type::size_type city = 0; city != tsp_instance.cities.size(); ++city)
395 {
396 while (city_usage[city] < 2)
397 {
398 do  next_city = rnd();
399 while (!has_valid_connection(tsp_instance, child, city_usage, city, next_city));
400 connect_cities(child, city_usage, city, next_city);
401 }
402 }
403
404 return child;
405 }
406
407 void mutate(tour& t, const random_functor& rnd)
408 {
409 int city_number = rnd();
410 auto& connection = t.connections[city_number];
411
412 int temp = connection.second;
413
414 if (first_ref(t, connection.first) == city_number)
415 {
416 if (first_ref(t, connection.second) == city_number)
417 first_ref(t, connection.second) = connection.first;
418 else
419 second_ref(t, connection.second) = connection.first;
420
421 first_ref(t, connection.first) = temp;
422 }
423 else
424 {
425 if (first_ref(t, connection.second) == city_number)
426 first_ref(t, connection.second) = connection.first;
427 else
428 second_ref(t, connection.second) = connection.first;
429
430 second_ref(t, connection.first) = temp;
431 }
432
433 int other_city_number = ­1;
434 do other_city_number = rnd(); while (other_city_number == city_number);
435 auto& other_connection = t.connections[other_city_number];
436
437 temp = other_connection.second;
438 connection.second = other_connection.second;
439 connection.first = other_city_number;
440 other_connection.second = city_number;
441
442 if (first_ref(t, temp) == other_city_number)
443 first_ref(t, temp) = city_number;
444 else
445 second_ref(t, temp) = city_number;
446 }
447
448 tour do_crossover_and_mutation(const tsp_class& tsp_instance, 
449 tour& current_best_tour, 
450 tour& first_parent,
451 tour& second_parent,                          
452 population_type::size_type mutation,
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\src\algorithms\genetic_algorithm.hpp 8
453 const random_functor& rnd_tour,
454 const random_functor& rnd_accept_mutation_percentage)
455 {
456 auto new_tour = crossover(tsp_instance, first_parent, second_parent, rnd_tour);
457 if (static_cast<unsigned int>(rnd_accept_mutation_percentage()) < mutation)
458 {
459 mutate(new_tour, rnd_tour);
460 new_tour.do_fitness();
461 }
462
463 if (new_tour.fitness() < current_best_tour.fitness())
464 current_best_tour = new_tour;
465
466 return new_tour;
467 }
468
469 void make_children_via_genetic_operation(const tsp_class& tsp_instance, 
470 tour& current_best_tour, 
471 population_type& population, 
472 population_type::size_type group_size, 
473 population_type::size_type mutation, 
474 const random_functor& rnd_population, 
475 const random_functor& rnd_tour, 
476 const random_functor& rnd_accept_mutation_percentage)
477 {
478 std::vector<population_type::size_type> tours;
479 tours.reserve(group_size);
480
481 for (unsigned int i = 0; i != group_size; ++i) 
482 tours.push_back(rnd_population());
483
484 std::sort(tours.begin(), tours.end(), [&population](const population_type::size_type& lhs, const 
population_type::size_type& rhs) {
485 return population[lhs].fitness() < population[rhs].fitness();
486 });
487
488 population[tours[group_size ­ 1]] = 
489 do_crossover_and_mutation(
490 tsp_instance, 
491 current_best_tour, 
492 population[tours[0]], 
493 population[tours[1]], 
494 mutation, 
495 rnd_tour, 
496 rnd_accept_mutation_percentage);
497
498 population[tours[group_size ­ 2]] =
499 do_crossover_and_mutation(
500 tsp_instance, 
501 current_best_tour, 
502 population[tours[1]], 
503 population[tours[0]],
504 mutation,
505 rnd_tour,
506 rnd_accept_mutation_percentage);
507 }
508
509 void genetic_algorithm(tsp_class& tsp_instance,
510 const unsigned int population_size,
511 const unsigned int mutation_percentage,
512 const unsigned int group_size,
513 const unsigned int number_of_generations,
514 const unsigned int nearby_cities,
515 const double nearby_cities_percentage,
516 const unsigned int seed)
517 {
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\src\algorithms\genetic_algorithm.hpp 9
518 auto closest_cities_table = make_closest_cities_table(tsp_instance, nearby_cities);
519 random_functor rnd_tour(static_cast<int>(tsp_instance.cities.size()), seed);
520
521 tour best_tour(tsp_instance);
522 auto population = make_population_via_selection(tsp_instance, best_tour, 
523 population_size, closest_cities_table, nearby_cities_percentage, 
524 rnd_tour, double_random_functor(), random_functor(nearby_cities));
525
526 random_functor rnd_population(static_cast<int>(population.size()), seed); 
527 random_functor rnd_accept_mutation_percentage(100, seed);
528
529 for(unsigned int g = 0; g < number_of_generations; ++g)
530 make_children_via_genetic_operation(tsp_instance, best_tour, population, group_size, 
mutation_percentage, 
531 rnd_population, rnd_tour, rnd_accept_mutation_percentage);
532
533 tsp_instance = tour_to_tsp_class(tsp_instance, best_tour);
534 }
535
536 #endif /* _genetic_algorithm_ */
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\src\algorithms\nearest_neighbour.hpp 1
1 //Sample provided by Fabio Galuppo
2 //Compositional TSP Solver version 0.1
3 //October 2013
4
5 #pragma once
6 #ifndef _nearest_neighbour_
7 #define _nearest_neighbour_
8
9 #include "..\tsp.hpp"
10
11 #include <algorithm>
12
13 void nearest_neighbour(tsp_class& tsp_instance)
14 {
15 auto xs = tsp_instance.cities;
16 tsp_instance.cities.clear();
17
18 auto x = *xs.begin();
19 tsp_instance.cities.push_back(x);
20 xs.erase(xs.begin());
21
22 while(xs.size() > 0)
23 {
24 auto nearest = std::min_element(xs.begin(), xs.end(), [&x](const tsp_class::city_info& i, const
tsp_class::city_info& j) {
25 return tsp_class::euclidean_distance(x.location, i.location) < tsp_class::euclidean_distance
(x.location, j.location);
26 });
27
28 x = *nearest;
29 tsp_instance.cities.push_back(x);
30 xs.erase(nearest);
31 }
32 }
33
34 #endif /* _nearest_neighbour_ */
C:\Users\Fabio Galuppo\Documents\Visual Studio ...TSPSolution\src\algorithms\simulated_annealing.hpp 1
1 //Sample provided by Fabio Galuppo
2 //Compositional TSP Solver version 0.1
3 //October 2013
4
5 #pragma once
6 #ifndef _simulated_annealing_
7 #define _simulated_annealing_
8
9 #include "..\tsp.hpp"
10
11 #include <cmath>
12
13 void simulated_annealing(const double initial_temperature,
14 const double stopping_criteria_temperature,
15 const double decreasing_factor,
16 const int monte_carlo_steps,
17 tsp_class& tsp_instance,
18 tsp_class::rand_function rnd_tsp,
19 std::function<double()> rnd_probability)
20 {
21 double temperature = initial_temperature;
22
23 while(temperature > stopping_criteria_temperature)
24 {
25 double cycle_length = tsp_instance.do_cycle_length();
26 tsp_class temp_tsp_instance = tsp_instance;
27
28 int i = monte_carlo_steps;
29 while(i­­ > 0)
30 {
31 temp_tsp_instance.do_pertubation(rnd_tsp, false);
32 double temp_cycle_length = temp_tsp_instance.do_cycle_length();
33
34 double dE = temp_cycle_length ­ cycle_length;
35
36 bool update = false;
37 if(dE < 0.0)
38 {
39 update = true;
40 }
41 else
42 {
43 //Inferior solution can be allowed to move from local optimal solution
44 //using probability of acceptance based on Boltzmann's function
45 auto boltzmannFunction = std::exp(­dE / temperature);
46 auto acceptanceProbability = rnd_probability();
47 update = boltzmannFunction > acceptanceProbability;
48 }
49
50 if(update)
51 {
52 tsp_instance = temp_tsp_instance;
53 cycle_length = temp_cycle_length;
54 }
55 }
56
57 temperature *= decreasing_factor;
58 }
59 }
60
61 #endif /* _simulated_annealing_ */
C:\Users\Fabio Galuppo\Documents\Visual Studio ...TSPSolution\TSPSolution\src\algorithms\two_opt.hpp 1
1 //Sample provided by Fabio Galuppo
2 //Compositional TSP Solver version 0.1
3 //October 2013
4
5 #pragma once
6 #ifndef _two_opt_
7 #define _two_opt_
8
9 #include "..\tsp.hpp"
10
11 #include <algorithm>
12 #include <cmath>
13
14 tsp_class two_opt_swap(const tsp_class& tsp_instance,
15 tsp_class::cities_type::size_type i,
16 tsp_class::cities_type::size_type k)
17 {
18 tsp_class temp_tsp_instance = tsp_instance;
19 std::reverse(temp_tsp_instance.cities.begin() + i, temp_tsp_instance.cities.begin() + k + 1);
20 return temp_tsp_instance;
21 }
22
23 #define DISTANCE_COMPARISON(v) static_cast<long>(std::floor(10000 * (v)))
24
25 void two_opt_all(tsp_class& tsp_instance)
26 {
27 bool can_continue;
28 do
29 {
30 can_continue = false;
31 auto best_distance = DISTANCE_COMPARISON(tsp_instance.do_cycle_length());
32
33 for(tsp_class::cities_type::size_type i = 1; i < tsp_instance.cities.size() ­ 1; ++i)
34 {
35 for(tsp_class::cities_type::size_type k = i + 1; k < tsp_instance.cities.size(); ++k)
36 {
37 auto new_route = two_opt_swap(tsp_instance, i, k);
38 auto new_distance = DISTANCE_COMPARISON(new_route.do_cycle_length());
39 if (new_distance < best_distance)
40 {
41 tsp_instance = new_route;
42 can_continue = true;
43 break;
44 }
45 }
46
47 if (can_continue) break;
48 }
49 } while(can_continue);
50 }
51
52 #endif /* _two_opt_ */
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\src\tsp_monad.hpp 1
1 //Sample provided by Fabio Galuppo
2 //Compositional TSP Solver version 0.1
3 //October 2013
4
5 #pragma once
6 #ifndef _tsp_monad_
7 #define _tsp_monad_
8
9 /*
10 class Monad m where
11 (>>=)  :: m a ­> (a ­> m b) ­> m b
12 return :: a ­> m a
13 */
14
15 //The monad laws ­ the three fundamental laws: 
16 //(return x) >>= f == f x
17 //m >>= return == m
18 //(m >>= f) >>= g == m >>= (\x ­> f x >>= g) 
19
20 //The first law requires that return is a left­identity with respect to >>= 
21 //The second law requires that return is a right­identity with respect to >>= 
22 //The third law is a kind of associativity law for >>=
23
24 #include "tsp.hpp"
25
26 #include <memory>
27 #include <utility>
28
29 struct base_args
30 {
31 base_args(int id) : Id_(id)
32 {
33 }
34
35 virtual ~base_args()
36 {
37 }
38
39 int get_id() const
40 {
41 return Id_;
42 }
43
44 private:
45 int Id_;
46 };
47
48 typedef std::pair<std::shared_ptr<tsp_class>, base_args*> Maybe;
49
50 Maybe just(tsp_class t) { return std::make_pair(std::make_shared<tsp_class>(t), nullptr); }
51 Maybe nothing() { std::shared_ptr<tsp_class> t(nullptr); return std::make_pair(t, nullptr); }
52 bool has_value(Maybe m) { return m.first.get() != nullptr; }
53 tsp_class& ref(Maybe m) { return *m.first; }
54 const tsp_class& cref(Maybe m) { return *m.first; }
55
56 //t: shared_ptr<tsp_class> <=> Maybe
57 //ret : t ­> TSP t
58 //bnd : TSP t ­> (t ­> TSP t) ­> TSP t
59 struct TSP
60 {
61 typedef Maybe T;
62 typedef std::function<TSP(T)> transformer_type;
63
64 explicit TSP(T t) : t(t) 
65 {
66 }
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\src\tsp_monad.hpp 2
67
68 //TODO: Semiregular
69
70 //Regular:
71 friend bool operator==(const TSP& lhs, const TSP& rhs) 
72 {
73 if (!has_value(lhs.t) && !has_value(rhs.t))
74 return true;
75
76 return *(lhs.t.first) == *(rhs.t.first);
77 }
78
79 friend bool operator!=(const TSP& lhs, const TSP& rhs)
80 {
81 return !(lhs == rhs);
82 }
83
84 //map
85 TSP map(transformer_type transformer) const
86 {
87 return bnd(*this, transformer);
88 }
89
90 //map
91 template <typename U>
92 U map(std::function<U(T)> transformer) const
93 {
94 return bnd(*this, transformer);
95 }
96
97 //return
98 static friend TSP ret(T a) 
99 { 
100 return TSP(just(*a.first));
101 }
102
103 //bind
104 static friend TSP bnd(TSP a, transformer_type transformer)
105 {
106 auto aa = a.t;
107 if (has_value(aa))
108 return transformer(aa);
109 return TSP(nothing());
110 }
111
112 //bind
113 template <typename U>
114 static friend U bnd(TSP a, std::function<U(T)> transformer)
115 {
116 if (has(a))
117 return transformer(a.t);
118 return U();
119
120 }
121
122 static friend tsp_class& ref(TSP a)
123 {
124 return ::ref(a.t);
125 }
126
127 static friend bool has(TSP a)
128 {
129 return has_value(a.t);
130 }
131
132 private:
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\src\tsp_monad.hpp 3
133 T t;
134 };
135
136 Maybe just(TSP t) { return just(ref(t)); }
137
138 #endif /* _tsp_monad_ */
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\TSPSolution\src\tsp_functors.hpp 1
1 //Sample provided by Fabio Galuppo
2 //Compositional TSP Solver version 0.1
3 //October 2013
4
5 #pragma once
6 #ifndef _tsp_functors_
7 #define _tsp_functors_
8
9 #include "tsp.hpp"
10 #include "tsp_monad.hpp"
11
12 TSP Propagator(const TSP& oldValue, const TSP& newValue, bool propagate_inferior_results = false)
13 {
14 //accept inferior results (if it's an inferior result!)
15 if (propagate_inferior_results) 
16 return newValue;
17
18 //do not propagate inferior results
19 if (ref(oldValue).do_cycle_length() > ref(newValue).do_cycle_length())
20 return newValue;
21
22 return oldValue;
23 };
24
25 enum base_args_id
26 {
27 FORK_JOIN_ARGS_ID = 1
28 };
29
30 struct Chain
31 {
32 typedef TSP::transformer_type F;
33
34 Chain(F f0)
35 {
36 Funcs_.push_back(f0);
37 }
38
39 Chain(F f0, F f1)
40 {
41 Funcs_.push_back(f0);
42 Funcs_.push_back(f1);
43 }
44
45 Chain(F f0, F f1, F f2)
46 {
47 Funcs_.push_back(f0);
48 Funcs_.push_back(f1);
49 Funcs_.push_back(f2);
50 }
51
52 Chain(F f0, F f1, F f2, F f3)
53 {
54 Funcs_.push_back(f0);
55 Funcs_.push_back(f1);
56 Funcs_.push_back(f2);
57 Funcs_.push_back(f3);
58 }
59
60 Chain(F f0, F f1, F f2, F f3, F f4)
61 {
62 Funcs_.push_back(f0);
63 Funcs_.push_back(f1);
64 Funcs_.push_back(f2);
65 Funcs_.push_back(f3);
66 Funcs_.push_back(f4);
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\TSPSolution\src\tsp_functors.hpp 2
67 }
68
69 Chain(F f0, F f1, F f2, F f3, F f4, F f5)
70 {
71 Funcs_.push_back(f0);
72 Funcs_.push_back(f1);
73 Funcs_.push_back(f2);
74 Funcs_.push_back(f3);
75 Funcs_.push_back(f4);
76 Funcs_.push_back(f5);
77 }
78
79 //...
80
81 TSP operator()(TSP::T t)
82 {
83 for(auto f = Funcs_.cbegin(); f != Funcs_.cend(); ++f)
84 (*f)(t);
85 return TSP(t);
86 }
87
88 private:
89 std::vector<F> Funcs_;
90 };
91
92 struct ForkJoin_args : public base_args
93 {
94 ForkJoin_args(unsigned int i, unsigned int number_of_tasks_in_parallel) : 
95 base_args(FORK_JOIN_ARGS_ID), 
96 i(i), 
97 number_of_tasks_in_parallel(number_of_tasks_in_parallel) {}
98 const unsigned int i;
99 const unsigned int number_of_tasks_in_parallel;
100 };
101
102 #include <future>
103 #include <memory>
104
105 struct ForkJoin
106 {
107 typedef TSP::transformer_type F;
108
109 ForkJoin(unsigned int number_of_tasks_in_parallel, F f, bool propagate_inferior_results = false) : 
110 NumberOfTasksInParallel_(number_of_tasks_in_parallel),
111 Func_(f),
112 PropagateInferiorResults_(propagate_inferior_results)
113 {
114 }
115
116 TSP operator()(TSP::T t)
117 {
118 if (0 == NumberOfTasksInParallel_)
119 return TSP(t);
120
121 std::vector<std::future<tsp_async_state>> tsp_solutions;
122
123 std::cout << "START SOLUTION:" << std::endl;
124
125 auto tsp_instance = ref(t);
126 display_solution(tsp_instance);
127
128 for (unsigned int i = 0; i < NumberOfTasksInParallel_; ++i)
129 {
130 //async
131 tsp_solutions.push_back(std::async(std::launch::async, [&, i, tsp_instance] 
132 {
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\TSPSolution\src\tsp_functors.hpp 3
133 Maybe a = just(tsp_instance);
134 std::unique_ptr<ForkJoin_args> args(new ForkJoin_args(i, NumberOfTasksInParallel_));
135 a.second = args.get();
136 return tsp_async_state(Func_(a));
137 }));
138 }
139
140 std::cout << "CANDIDATE SOLUTIONS:" << std::endl;
141 std::vector<tsp_class> candidate_solutions;
142 int i = 0;
143 for (auto& tsp_solution : tsp_solutions)
144 {
145 //async result
146 auto candidate = tsp_solution.get();
147 if (has(candidate.state))
148 {
149 candidate_solutions.push_back(ref(candidate.state));
150 display(ref(candidate.state), false);
151 }
152 }
153
154 std::cout << "CANDIDATE LENGTHS:" << std::endl;
155 for (auto& solution : candidate_solutions)
156 std::cout << solution.do_cycle_length() << std::endl;
157
158 std::sort(candidate_solutions.begin(), candidate_solutions.end(), 
159 [](const tsp_class& left, const tsp_class& right) 
160 { return left.do_cycle_length() < right.do_cycle_length(); });
161
162 std::cout << "CANDIDATE LENGTHS (ORDERED):" << std::endl;
163 for (auto& solution : candidate_solutions)
164 std::cout << solution.do_cycle_length() << std::endl;
165
166 std::cout << "SELECTED SOLUTION:" << std::endl;
167 auto result = Propagator(TSP(t), TSP(just(*candidate_solutions.begin())), 
PropagateInferiorResults_);
168 display_solution(ref(result));
169 return result;
170 }
171
172 private:
173 unsigned int NumberOfTasksInParallel_;
174 F Func_;
175 bool PropagateInferiorResults_;
176
177 static void display_solution(tsp_class& tsp_instance)
178 {
179 display(tsp_instance, true);
180 std::cout << tsp_instance.do_cycle_length() << std::endl;
181 }
182
183 struct tsp_async_state
184 {
185 tsp_async_state() 
186 : state(TSP(nothing())) {}
187 explicit tsp_async_state(const TSP& state) 
188 : state(state) {}
189 TSP state;
190 };
191 };
192
193 struct Generations
194 {
195 typedef TSP::transformer_type F;
196
197 Generations(unsigned int number_of_generations, F f) : 
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\TSPSolution\src\tsp_functors.hpp 4
198 NumberOfGenerations_(number_of_generations),
199 Func_(f)
200 {
201 }
202
203 TSP operator()(TSP::T t)
204 {
205 TSP::T result = t; 
206 for(unsigned int g = 0; g < NumberOfGenerations_; ++g) 
207 {
208 std::cout << "GENERATION:" << g + 1 << std::endl;
209 auto new_result = Func_(result);
210 result = just(new_result);
211 }
212 return TSP(result);
213 }
214
215 private:
216 unsigned int NumberOfGenerations_;
217 F Func_;
218 };
219
220 struct Identity
221 {
222 TSP operator()(TSP::T t)
223 {
224 return TSP(t);
225 }
226 };
227
228 #include <atomic>
229
230 struct Circular
231 {
232 typedef TSP::transformer_type F;
233
234 Circular(F f0) : 
235 MaxCount_(1)
236 {
237 Counter_ = 0;
238 Funcs_.push_back(f0);
239 }
240
241 Circular(F f0, F f1) :
242 MaxCount_(2)
243 {
244 Counter_ = 0;
245 Funcs_.push_back(f0);
246 Funcs_.push_back(f1);
247 }
248
249 Circular(F f0, F f1, F f2) :
250 MaxCount_(3)
251 {
252 Counter_ = 0;
253 Funcs_.push_back(f0);
254 Funcs_.push_back(f1);
255 Funcs_.push_back(f2);
256 }
257
258 Circular(F f0, F f1, F f2, F f3) :
259 MaxCount_(4)
260 {
261 Counter_ = 0;
262 Funcs_.push_back(f0);
263 Funcs_.push_back(f1);
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\TSPSolution\src\tsp_functors.hpp 5
264 Funcs_.push_back(f2);
265 Funcs_.push_back(f3);
266 }
267
268 Circular(F f0, F f1, F f2, F f3, F f4) :
269 MaxCount_(5)
270 {
271 Counter_ = 0;
272 Funcs_.push_back(f0);
273 Funcs_.push_back(f1);
274 Funcs_.push_back(f2);
275 Funcs_.push_back(f3);
276 Funcs_.push_back(f4);
277 }
278
279 Circular(F f0, F f1, F f2, F f3, F f4, F f5) :
280 MaxCount_(6)
281 {
282 Counter_ = 0;
283 Funcs_.push_back(f0);
284 Funcs_.push_back(f1);
285 Funcs_.push_back(f2);
286 Funcs_.push_back(f3);
287 Funcs_.push_back(f4);
288 Funcs_.push_back(f5);
289 }
290
291 //...
292
293 TSP operator()(TSP::T t)
294 {
295 Funcs_[Counter_++ % MaxCount_](t);
296 return TSP(t);
297 }
298
299 private:
300 std::vector<F> Funcs_;
301 std::atomic_int Counter_;
302 const unsigned int MaxCount_;
303 };
304
305 #endif /* _tsp_functors_ */
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\src\tsp_algo.hpp 1
1 //Sample provided by Fabio Galuppo
2 //Compositional TSP Solver version 0.1
3 //October 2013
4
5 #pragma once
6 #ifndef _tsp_algo_
7 #define _tsp_algo_
8
9 #include "tsp.hpp"
10 #include "tsp_monad.hpp"
11 #include "tsp_functors.hpp"
12 #include "algorithms\ant_colony_optimization.hpp"
13 #include "algorithms\simulated_annealing.hpp"
14 #include "algorithms\nearest_neighbour.hpp"
15 #include "algorithms\two_opt.hpp"
16 #include "algorithms\genetic_algorithm.hpp"
17 #include "randomizers.hpp"
18
19 void get_base_args(unsigned int& i, unsigned int& n, base_args* args)
20 {
21 if (nullptr != args)
22 {
23 if (args­>get_id() == base_args_id::FORK_JOIN_ARGS_ID)
24 {
25 auto ptr = dynamic_cast<ForkJoin_args*>(args);
26 i = ptr­>i;
27 n = ptr­>number_of_tasks_in_parallel;
28 }
29 }
30 }
31
32 struct SA
33 {
34 SA(const double initial_temperature,
35 const double stopping_criteria_temperature,
36 const double decreasing_factor, 
37 const int monte_carlo_steps) :
38 InitialTemperature_(initial_temperature),
39 StoppingCriteriaTemperature_(stopping_criteria_temperature),
40 DecreasingFactor_(decreasing_factor),
41 MonteCarloSteps_(monte_carlo_steps)
42 { 
43 }
44
45 TSP operator()(TSP::T t)
46 {
47 unsigned int i = 0, n = 1;
48 get_base_args(i, n, t.second);
49
50 const auto N = ref(t).cities.size();   
51 random_functor delay(1, static_cast<int>(n * 2));
52 auto seed = static_cast<unsigned>(std::time(nullptr));
53 auto rnd_tsp(random_functor(N, seed + i * delay()));
54
55 simulated_annealing(InitialTemperature_, StoppingCriteriaTemperature_, 
56 DecreasingFactor_, MonteCarloSteps_, ref(t),
57 rnd_tsp, double_random_functor());
58 return TSP(t);
59 }
60
61 private:
62 const double InitialTemperature_;
63 const double StoppingCriteriaTemperature_;
64 const double DecreasingFactor_;
65 const int MonteCarloSteps_;
66 };
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\src\tsp_algo.hpp 2
67
68 #include <vector>
69
70 struct ACO
71 {
72 ACO(const unsigned int aco_iterations, 
73 const ants_type::size_type number_of_ants, 
74 const double base_pheromone,
75 const double favor_pheromone_level_over_distance, 
76 const double favor_distance_over_pheromone_level, 
77 const double value_for_intensification_and_evaporation, 
78 const double pheronome_distribution) : 
79 ACOIterations_(aco_iterations),
80 NumberOfAnts_(number_of_ants),
81 BasePheromone_(base_pheromone),
82 FavorPheromoneLevelOverDistance_(favor_pheromone_level_over_distance), 
83 FavorDistanceOverPheromoneLevel_(favor_distance_over_pheromone_level), 
84 ValueForIntensificationAndEvaporation_(value_for_intensification_and_evaporation),
85 PheronomeDistribution_(pheronome_distribution)
86 {
87 }
88
89 TSP operator()(TSP::T t)
90 {
91 unsigned int i = 0, n = 1;
92 get_base_args(i, n, t.second);
93
94 auto seed = static_cast<unsigned>(std::time(nullptr));
95 random_functor delay(1, static_cast<int>(n * 10));
96
97 ant_colony_optimization(ref(t), BasePheromone_, ACOIterations_, NumberOfAnts_, 
98 FavorPheromoneLevelOverDistance_, FavorDistanceOverPheromoneLevel_,
99 ValueForIntensificationAndEvaporation_, PheronomeDistribution_, seed + 
i * delay());
100 return TSP(t);
101 };
102
103 private: 
104 const double BasePheromone_;
105 const unsigned int ACOIterations_;
106 const ants_type::size_type NumberOfAnts_; 
107 const double FavorPheromoneLevelOverDistance_; 
108 const double FavorDistanceOverPheromoneLevel_; 
109 const double ValueForIntensificationAndEvaporation_; 
110 const double PheronomeDistribution_;
111 };
112
113 struct _2OPT
114 {
115 TSP operator()(TSP::T t)
116 {
117 two_opt_all(ref(t));
118 return TSP(t);
119 }
120 };
121
122 struct NN
123 {
124 TSP operator()(TSP::T t)
125 {
126 nearest_neighbour(ref(t));
127 return TSP(t);
128 };
129 };
130
131 struct GA
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\src\tsp_algo.hpp 3
132 {
133 GA(const unsigned int population_size,
134 const unsigned int mutation_percentage,
135 const unsigned int group_size,
136 const unsigned int number_of_generations,
137 const unsigned int nearby_cities,
138 const double nearby_cities_percentage) :
139 PopulationSize_(population_size),
140 MutationPercentage_(mutation_percentage),
141 GroupSize_(group_size),
142 NumberOfGenerations_(number_of_generations),
143 NearbyCities_(nearby_cities),
144 NearbyCitiesPercentage_(nearby_cities_percentage)
145 {
146 }
147
148 TSP operator()(TSP::T t)
149 {
150 unsigned int i = 0, n = 1;
151 get_base_args(i, n, t.second);
152
153 auto seed = static_cast<unsigned>(std::time(nullptr));
154 random_functor delay(1, static_cast<int>(n * 10));
155
156 genetic_algorithm(ref(t), PopulationSize_, MutationPercentage_, 
157 GroupSize_, NumberOfGenerations_, NearbyCities_, NearbyCitiesPercentage_, seed + i * delay
());
158 return TSP(t);
159 }
160
161 private:
162 const unsigned int PopulationSize_;
163 const unsigned int MutationPercentage_;
164 const unsigned int GroupSize_;
165 const unsigned int NumberOfGenerations_;
166 const unsigned int NearbyCities_;
167 const double NearbyCitiesPercentage_;
168 };
169
170 #endif /* _tsp_algo_ */
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\TSPSolution\src\_tsp_support.hpp 1
1 //Sample provided by Fabio Galuppo
2 //Compositional TSP Solver version 0.1
3 //October 2013
4
5 #pragma once
6 #ifndef __tsp_support_
7 #define __tsp_support_
8
9 struct GeneralArgs
10 {
11 GeneralArgs(const unsigned int number_of_iterations_or_generations, const unsigned int
number_of_tasks_in_parallel) :
12 number_of_iterations_or_generations(number_of_iterations_or_generations),
13 number_of_tasks_in_parallel(number_of_tasks_in_parallel)
14 {
15 }
16
17 const unsigned int number_of_iterations_or_generations;
18 const unsigned int number_of_tasks_in_parallel;
19 };
20
21 struct SAArgs
22 {
23 SAArgs(const double initial_temperature,
24 const double stopping_criteria_temperature,
25 const double decreasing_factor,
26 const int monte_carlo_steps) :
27 initial_temperature(initial_temperature),
28 stopping_criteria_temperature(stopping_criteria_temperature),
29 decreasing_factor(decreasing_factor),
30 monte_carlo_steps(monte_carlo_steps)
31 {
32 }
33
34 const double initial_temperature;
35 const double stopping_criteria_temperature;
36 const double decreasing_factor;
37 const int monte_carlo_steps;
38 };
39
40 struct ACOArgs
41 {
42 ACOArgs(const unsigned int aco_iterations,
43 const ants_type::size_type number_of_ants,
44 const double base_pheromone,
45 const double favor_pheromone_level_over_distance,
46 const double favor_distance_over_pheromone_level,
47 const double value_for_intensification_and_evaporation,
48 const double pheronome_distribution) :
49 aco_iterations(aco_iterations),
50 number_of_ants(number_of_ants),
51 base_pheromone(base_pheromone),
52 favor_pheromone_level_over_distance(favor_pheromone_level_over_distance),
53 favor_distance_over_pheromone_level(favor_distance_over_pheromone_level),
54 value_for_intensification_and_evaporation(value_for_intensification_and_evaporation),
55 pheronome_distribution(pheronome_distribution)
56 {
57 }
58
59 const unsigned int aco_iterations;
60 const ants_type::size_type number_of_ants;
61 const double base_pheromone;
62 const double favor_pheromone_level_over_distance;
63 const double favor_distance_over_pheromone_level;
64 const double value_for_intensification_and_evaporation;
65 const double pheronome_distribution;
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\TSPSolution\src\_tsp_support.hpp 2
66 };
67
68 struct GAArgs
69 {
70 GAArgs(const unsigned int population_size,
71 const unsigned int mutation_percentage,
72 const unsigned int group_size,
73 const unsigned int number_of_generations,
74 const unsigned int nearby_cities,
75 const double nearby_cities_percentage) :
76 population_size(population_size),
77 mutation_percentage(mutation_percentage),
78 group_size(group_size),
79 number_of_generations(number_of_generations),
80 nearby_cities(nearby_cities),
81 nearby_cities_percentage(nearby_cities_percentage)
82 {
83 }
84
85 const unsigned int population_size;
86 const unsigned int mutation_percentage;
87 const unsigned int group_size;
88 const unsigned int number_of_generations;
89 const unsigned int nearby_cities;
90 const double nearby_cities_percentage;
91 };
92
93 typedef GeneralArgs General_args_type;
94 typedef SAArgs SA_args_type;
95 typedef ACOArgs ACO_args_type;
96 typedef GAArgs GA_args_type;
97
98 General_args_type
99 make_General_args(const unsigned int number_of_iterations_or_generations, const unsigned int
number_of_tasks_in_parallel)
100 {
101 return General_args_type(number_of_iterations_or_generations, number_of_tasks_in_parallel);
102 }
103
104 SA_args_type
105 make_SA_args(const double initial_temperature,
106 const double stopping_criteria_temperature,
107 const double decreasing_factor,
108 const int monte_carlo_steps)
109 {
110 return SA_args_type(initial_temperature, stopping_criteria_temperature, decreasing_factor,
monte_carlo_steps);
111 }
112
113 ACO_args_type
114 make_ACO_args(const unsigned int aco_iterations,
115 const ants_type::size_type number_of_ants,
116 const double base_pheromone,
117 const double favor_pheromone_level_over_distance,
118 const double favor_distance_over_pheromone_level,
119 const double value_for_intensification_and_evaporation,
120 const double pheronome_distribution)
121 {
122 return ACO_args_type(aco_iterations, number_of_ants, base_pheromone,
123 favor_pheromone_level_over_distance, favor_distance_over_pheromone_level,
124 value_for_intensification_and_evaporation, pheronome_distribution);
125 }
126
127 GA_args_type
128 make_GA_args(const unsigned int population_size,
129 const unsigned int mutation_percentage,
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\TSPSolution\src\_tsp_support.hpp 3
130 const unsigned int group_size,
131 const unsigned int number_of_generations,
132 const unsigned int nearby_cities,
133 const double nearby_cities_percentage)
134 {
135 return GA_args_type(population_size, mutation_percentage, group_size,
136 number_of_generations, nearby_cities, nearby_cities_percentage);
137 }
138
139 #include <vector>
140 typedef std::vector<General_args_type> General_args_type_collection;
141 typedef std::vector<SA_args_type> SA_args_type_collection;
142 typedef std::vector<ACO_args_type> ACO_args_type_collection;
143 typedef std::vector<GA_args_type> GA_args_type_collection;
144
145 void display_args(const char* description,
146 const General_args_type_collection& gs,
147 const SA_args_type_collection& sas,
148 const ACO_args_type_collection& acos,
149 const GA_args_type_collection& gas)
150 {
151 std::cout << "SOLUTION ARGUMENTS:" << std::endl;
152
153 std::cout << description << std::endl;
154
155 if (gs.size() > 0)
156 {
157 std::cout << "GENERAL ARGUMENTS:" << std::endl;
158 int count = 0;
159 for(auto i = gs.cbegin(); i != gs.cend(); ++i)
160 {
161 std::cout << " #" << ++count << ":"<< std::endl;
162 std::cout << " Number of Iterations or Generations = " << i->
number_of_iterations_or_generations << std::endl;
163 std::cout << " Number of Tasks in Parallel = " << i->number_of_tasks_in_parallel << std::
endl;
164 //std::cout << "Propagate Inferior Results Across Generations = " << (p ? "true" : "false")
<< std::endl;
165 }
166 }
167
168 if (sas.size() > 0)
169 {
170 std::cout << "SIMULATED ANNEALING ARGUMENTS:" << std::endl;
171 int count = 0;
172 for(auto i = sas.cbegin(); i != sas.cend(); ++i)
173 {
174 std::cout << " #" << ++count << ":"<< std::endl;
175 std::cout << " Initial Temperature = " << i->initial_temperature << std::endl;
176 std::cout << " Stopping Criteria Temperature = " << i->stopping_criteria_temperature <<
std::endl;
177 std::cout << " Decreasing Factor = " << i->decreasing_factor << std::endl;
178 std::cout << " Monte Carlo Steps = " << i->monte_carlo_steps << std::endl;
179 }
180 }
181
182 if (acos.size() > 0)
183 {
184 std::cout << "ANT COLONY OPTIMIZATION ARGUMENTS:" << std::endl;
185 int count = 0;
186 for(auto i = acos.cbegin(); i != acos.cend(); ++i)
187 {
188 std::cout << " #" << ++count << ":"<< std::endl;
189 std::cout << " ACO Iterations = " << i->aco_iterations << std::endl;
190 std::cout << " Number of Ants = " << i->number_of_ants << std::endl;
191 std::cout << " Base Pheromone = " << i->base_pheromone << std::endl;
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\TSPSolution\src\_tsp_support.hpp 4
192 std::cout << " Favor Pheromone Level over Distance = " << i->
favor_pheromone_level_over_distance << std::endl;
193 std::cout << " Favor Distance over Pheromone Level = " << i->
favor_distance_over_pheromone_level << std::endl;
194 std::cout << " Intensification and Evaporation Value = " << i->
value_for_intensification_and_evaporation << std::endl;
195 std::cout << " Pheromone Distribution = " << i->pheronome_distribution << std::endl;
196 }
197 }
198
199 if (gas.size() > 0)
200 {
201 std::cout << "GENETIC ALGORITHM ARGUMENTS:" << std::endl;
202 int count = 0;
203 for(auto i = gas.cbegin(); i != gas.cend(); ++i)
204 {
205 std::cout << " #" << ++count << ":"<< std::endl;
206 std::cout << " Population Size = " << i->population_size << std::endl;
207 std::cout << " Mutation Percentage = " << i->mutation_percentage << std::endl;
208 std::cout << " Group Size = " << i->group_size << std::endl;
209 std::cout << " Number of Generations = " << i->number_of_generations << std::endl;
210 std::cout << " Nearby Cities = " << i->nearby_cities << std::endl;
211 std::cout << " Nearby Cities Percentage = " << i->nearby_cities_percentage << std::endl;
212 }
213 }
214
215 std::cout << "--------------------------------------------------" << std::endl;
216 }
217
218 template <typename A>
219 struct Args
220 {
221 typedef std::vector<A> collection_type;
222
223 operator collection_type() const
224 {
225 return collection;
226 }
227
228 Args()
229 {
230 }
231
232 Args(A a0)
233 {
234 collection.push_back(a0);
235 }
236
237 Args(A a0, A a1)
238 {
239 collection.push_back(a0);
240 collection.push_back(a1);
241 }
242
243 Args(A a0, A a1, A a2)
244 {
245 collection.push_back(a0);
246 collection.push_back(a1);
247 collection.push_back(a2);
248 }
249
250 Args(A a0, A a1, A a2, A a3)
251 {
252 collection.push_back(a0);
253 collection.push_back(a1);
254 collection.push_back(a2);
C:\Users\Fabio Galuppo\Documents\Visual Studio ...\TSPSolution\TSPSolution\src\_tsp_support.hpp 5
255 collection.push_back(a3);
256 }
257
258 Args(A a0, A a1, A a2, A a3, A a4)
259 {
260 collection.push_back(a0);
261 collection.push_back(a1);
262 collection.push_back(a2);
263 collection.push_back(a3);
264 collection.push_back(a4);
265 }
266
267 Args(A a0, A a1, A a2, A a3, A a4, A a5)
268 {
269 collection.push_back(a0);
270 collection.push_back(a1);
271 collection.push_back(a2);
272 collection.push_back(a3);
273 collection.push_back(a4);
274 collection.push_back(a5);
275 }
276
277 //...
278
279 typename collection_type::const_reference operator[](typename collection_type::size_type index)
const
280 {
281 return collection[index];
282 }
283
284 typename collection_type::reference operator[](typename collection_type::size_type index)
285 {
286 return collection[index];
287 }
288
289 collection_type collection;
290 };
291
292 #endif /* __tsp_support_ */
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\src\_tsp.hpp 1
1 //Sample provided by Fabio Galuppo
2 //Compositional TSP Solver version 0.1
3 //October 2013
4
5 #pragma once
6 #ifndef __tsp_
7 #define __tsp_
8
9 #include "tsp.hpp"
10 #include "tsp_algo.hpp"
11
12 enum struct DisplayFlags
13 {
14 None = 0,
15 EmitMathematicaGraphPlot = 1,
16 DisplaySeparator = 2,
17 All = EmitMathematicaGraphPlot | DisplaySeparator
18 };
19
20 struct Display
21 {
22 Display(const char* text, DisplayFlags flags = DisplayFlags::None)
23 : Text_(text), DisplayFlags_(flags)
24 {
25 }
26
27 TSP operator()(TSP::T t)
28 {
29 std::cout << Text_ << ":" << std::endl;
30 display(ref(t), has_flag(DisplayFlags::EmitMathematicaGraphPlot));
31 if (has_flag(DisplayFlags::DisplaySeparator))
32 std::cout << "­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­" << std::endl;
33 return TSP(t);
34 }
35
36 private:
37 bool has_flag(DisplayFlags flag)
38 {
39 return static_cast<unsigned int>(flag) == (static_cast<unsigned int>(DisplayFlags_) &
static_cast<unsigned int>(flag));
40 }
41
42 const char* Text_;
43 DisplayFlags DisplayFlags_;
44 };
45
46 #include <chrono>
47
48 struct stop_watch
49 {
50 stop_watch() :
51 Start_(now())
52 {
53 }
54
55 std::chrono::milliseconds elapsed_ms() const
56 {
57 return std::chrono::duration_cast<std::chrono::milliseconds>(now() ­ Start_);
58 }
59
60 void restart() { Start_ = now(); }
61
62 private:
63 static std::chrono::high_resolution_clock::time_point now()
64 {
65 return std::chrono::high_resolution_clock::now();
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\src\_tsp.hpp 2
66 }
67
68 std::chrono::high_resolution_clock::time_point Start_;
69 };
70
71 struct Measure
72 {
73 typedef TSP::transformer_type F;
74
75 Measure(F f, F p = Identity()) : AlgoFunc_(f), PrintFunc_(p)
76 {
77 }
78
79 TSP operator()(TSP::T t)
80 {
81 stop_watch sw;
82 auto result = AlgoFunc_(t);
83 auto elapsed_ms = sw.elapsed_ms().count();
84 bnd(result, PrintFunc_);
85 std::cout << elapsed_ms << " ms" << std::endl;
86 std::cout << "­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­" << std::endl;
87 return result;
88 }
89
90 private:
91 F AlgoFunc_, PrintFunc_;
92 };
93
94 #include <cstdlib>
95
96 tsp_class read_tsp_instance()
97 {
98 tsp_class tsp_instance;
99
100 //read an instance of tsp
101 int N;
102 std::cin >> N;
103 if (N > 1)
104 {
105 int city_number = 0;
106 for(int i = 0; i < N; ++i)
107 {
108 int x, y;
109 std::cin >> x >> y;
110 tsp_class::city_info info = { ++city_number, std::make_pair(x, y) };
111 tsp_instance.cities.push_back(info);
112 }
113 }
114 else
115 {
116 std::cerr << "N must be greater than 1" << std::endl;
117 std::exit(1);
118 }
119
120 return tsp_instance;
121 }
122
123 #include "_tsp_support.hpp"
124
125 #endif /* __tsp_ */
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\program.cpp 1
1 //Sample provided by Fabio Galuppo
2 //Compositional TSP Solver version 0.1
3 //November 2013
4
5 #include "src\_tsp.hpp"
6
7 tsp_class read_att48_tsp()
8 {
9 int coords[] = {
10 6734, 1453,
11 2233, 10,
12 5530, 1424,
13 401, 841,
14 3082, 1644,
15 7608, 4458,
16 7573, 3716,
17 7265, 1268,
18 6898, 1885,
19 1112, 2049,
20 5468, 2606,
21 5989, 2873,
22 4706, 2674,
23 4612, 2035,
24 6347, 2683,
25 6107, 669,
26 7611, 5184,
27 7462, 3590,
28 7732, 4723,
29 5900, 3561,
30 4483, 3369,
31 6101, 1110,
32 5199, 2182,
33 1633, 2809,
34 4307, 2322,
35 675, 1006,
36 7555, 4819,
37 7541, 3981,
38 3177, 756,
39 7352, 4506,
40 7545, 2801,
41 3245, 3305,
42 6426, 3173,
43 4608, 1198,
44 23, 2216,
45 7248, 3779,
46 7762, 4595,
47 7392, 2244,
48 3484, 2829,
49 6271, 2135,
50 4985, 140,
51 1916, 1569,
52 7280, 4899,
53 7509, 3239,
54 10, 2676,
55 6807, 2993,
56 5185, 3258,
57 3023, 1942
58 };
59
60 tsp_class tsp_instance;
61 int N = sizeof(coords) / sizeof(int);
62 int city_number = 0;
63 for(int i = 0; i < N; i+=2)
64 {
65 int x = coords[i], y = coords[i+1];
66 tsp_class::city_info info = { ++city_number, std::make_pair(x, y) };
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\program.cpp 2
67 tsp_instance.cities.push_back(info);
68 }
69
70 return tsp_instance;
71 }
72
73 /*
74 getopt
75 declaration: http://svnweb.freebsd.org/base/stable/9/include/unistd.h?view=markup
76 definition: http://svnweb.freebsd.org/base/stable/9/lib/libc/stdlib/getopt.c?view=markup
77 */
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <string.h>
81
82 int opterr = 1, optind = 1, optopt, optreset;
83 char *optarg;
84
85 #define BADCH (int)'?'
86 #define BADARG (int)':'
87 #define EMSG ""
88
89 /*
90 * getopt --
91 * Parse argc/argv argument vector.
92 */
93 int getopt(int nargc, char * const nargv[], const char *ostr)
94 {
95 static char *place = EMSG; /* option letter processing */
96 char *oli; /* option letter list index */
97
98 if (optreset || *place == 0) { /* update scanning pointer */
99 optreset = 0;
100 place = nargv[optind];
101 if (optind >= nargc || *place++ != '-') {
102 /* Argument is absent or is not an option */
103 place = EMSG;
104 return (-1);
105 }
106 optopt = *place++;
107 if (optopt == '-' && *place == 0) {
108 /* "--" => end of options */
109 ++optind;
110 place = EMSG;
111 return (-1);
112 }
113 if (optopt == 0) {
114 /* Solitary '-', treat as a '-' option
115 if the program (eg su) is looking for it. */
116 place = EMSG;
117 if (strchr(ostr, '-') == NULL)
118 return (-1);
119 optopt = '-';
120 }
121 } else
122 optopt = *place++;
123
124 /* See if option letter is one the caller wanted... */
125 //if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) {
126 if (optopt == ':' || (oli = const_cast<char*>(strchr(ostr, optopt))) == NULL) {
127 if (*place == 0)
128 ++optind;
129 if (opterr && *ostr != ':')
130 (void)fprintf(stderr,
131 //"%s: illegal option -- %c\n", _getprogname(),
132 "%s: illegal option -- %c\n", "getopt",
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\program.cpp 3
133 optopt);
134 return (BADCH);
135 }
136
137 /* Does this option need an argument? */
138 if (oli[1] != ':') {
139 /* don't need argument */
140 optarg = NULL;
141 if (*place == 0)
142 ++optind;
143 } else {
144 /* Option-argument is either the rest of this argument or the
145 entire next argument. */
146 if (*place)
147 optarg = place;
148 else if (nargc > ++optind)
149 optarg = nargv[optind];
150 else {
151 /* option-argument absent */
152 place = EMSG;
153 if (*ostr == ':')
154 return (BADARG);
155 if (opterr)
156 (void)fprintf(stderr,
157 "%s: option requires an argument -- %c\n",
158 //_getprogname(), optopt);
159 "getopt", optopt);
160 return (BADCH);
161 }
162 place = EMSG;
163 ++optind;
164 }
165 return (optopt); /* return option letter */
166 }
167
168 #include <iostream>
169
170 void usage(void)
171 {
172 std::cerr << "usage: tspsolution -p:N -g:N -n:N" << std::endl;
173 std::cerr << "where" << std::endl;
174 std::cerr << " p: pipeline number" << std::endl;
175 std::cerr << " g: number of generations" << std::endl;
176 std::cerr << " n: number of tasks" << std::endl;
177 std::cerr << " N: an integer greater than 0" << std::endl;
178
179 exit(1);
180 }
181
182 void get_args(int& argc, char* argv[], unsigned int& p, unsigned int& g, unsigned int& n)
183 {
184 int ch;
185 setlocale(LC_CTYPE, "");
186 while ((ch = getopt(argc, argv, "p:g:n:")) != -1)
187 {
188 switch((char)ch)
189 {
190 case 'p': //pipeline #
191 ++optarg;
192 p = static_cast<unsigned int>(atol(optarg));
193 break;
194 case 'g': //generations #
195 ++optarg;
196 g = static_cast<unsigned int>(atol(optarg));
197 break;
198 case 'n': //tasks #
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\program.cpp 4
199 ++optarg;
200 n = static_cast<unsigned int>(atol(optarg));
201 break;
202 case '?':
203 default:
204 usage();
205 }
206 }
207
208 if (0 == p || 0 == g || 0 == n)
209 usage();
210
211 argv += optind;
212 argc -= optind;
213 }
214
215 #include <utility>
216 #include <functional>
217 #include <vector>
218
219 typedef std::function<void(tsp_class&, unsigned int, unsigned int)> pipeline_type;
220
221 TSP make_TSP(const tsp_class& tsp_instance) { return TSP(just(tsp_instance)); }
222
223 void MonadicLawsProofing()
224 {
225 //Scala notation: unit(x) flatMap f == f(x)
226 {
227 //Left Identity (1st Law)
228 auto x = just(read_att48_tsp());
229
230 auto f = [](TSP::T t){ return ref(t).do_cycle_length(); };
231
232 double fun_result = bnd<double>(ret(x), f); //functional composition
233 double oo_result = ret(x).map<double>(f); //object-oriented
234
235 bool fun_holds = fun_result == f(x);
236 bool oo_holds = oo_result == f(x);
237
238 std::cout << "1st Law (Left Identity) " << (fun_holds ? "holds" : "doesn't hold") << std::endl;
239 std::cout << "1st Law (Left Identity) " << (oo_holds ? "holds" : "doesn't hold") << std::endl;
240 }
241
242 //Scala notation: m flatMap unit == m
243 {
244 //Right Identity (2nd Law)
245 auto m = make_TSP(read_att48_tsp());
246
247 auto fun_result = bnd(m, ret);
248 auto oo_result = m.map(ret);
249
250 bool fun_holds = fun_result == m;
251 bool oo_holds = oo_result == m;
252
253 std::cout << "2nd Law (Right Identity) " << (fun_holds ? "holds" : "doesn't hold") << std::endl
;
254 std::cout << "2nd Law (Right Identity) " << (oo_holds ? "holds" : "doesn't hold") << std::endl;
255 }
256
257 //Scala notation: m flatMap f flatMap g == m flatMap (x => f(x) flatMap g)
258 {
259 //Associativity (3rd Law)
260 auto m = make_TSP(read_att48_tsp());
261
262 auto f = [](TSP::T t) -> TSP
263 {
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\program.cpp 5
264 auto u = ref(t);
265 std::swap(u.cities[0], u.cities[1]);
266 return make_TSP(u);
267 };
268
269 auto g = [](TSP::T t) -> TSP
270 {
271 auto u = ref(t);
272 std::swap(u.cities[2], u.cities[3]);
273 return make_TSP(u);
274 };
275
276 auto fun_result_1 = bnd(bnd(m, f), g);
277 auto fun_result_2 = bnd(m, [&](TSP::T t) {
278 return bnd(f(t), g);
279 });
280
281 auto oo_result_1 = m.map(f).map(g);
282 auto oo_result_2 = m.map([&](TSP::T t) {
283 return f(t).map(g);
284 });
285
286 bool fun_holds = fun_result_1 == fun_result_2;
287 bool oo_holds = oo_result_1 == oo_result_2;
288
289 std::cout << "3rd Law (Associativity) " << (fun_holds ? "holds" : "doesn't hold") << std::endl;
290 std::cout << "3rd Law (Associativity) " << (oo_holds ? "holds" : "doesn't hold") << std::endl;
291 }
292 }
293
294 //TSP -> NN -> Generations( g, ForkJoin ( n, SA -> 2-OPT ) ) -> TSP'
295 void Pipeline1(tsp_class& tsp_instance, unsigned int number_of_tasks, unsigned int
number_of_generations)
296 {
297 #pragma region "PipelineConfiguration"
298 auto a = Args<General_args_type>(make_General_args(number_of_generations, number_of_tasks));
299 auto sa = Args<SA_args_type>(make_SA_args(1000.0, 0.00001, 0.999, 400));
300 auto aco = Args<ACO_args_type>();
301 auto ga = Args<GA_args_type>();
302
303 const char* pipeline_description = "TSP -> NN -> Generations( g, ForkJoin ( n, SA -> 2-OPT ) ) ->
TSP'";
304 display_args(pipeline_description, a, sa, aco, ga);
305
306 auto g = a[0].number_of_iterations_or_generations;
307 auto n = a[0].number_of_tasks_in_parallel;
308 auto _TSP = TSP(just(tsp_instance));
309 auto _DisplayInput = Display("TSP INPUT", DisplayFlags::All);
310 auto _NN = Measure(NN(), Display("NEAREST NEIGHBOUR", DisplayFlags::EmitMathematicaGraphPlot));
311 auto _SA_2OPT = Chain(SA(sa[0].initial_temperature, sa[0].stopping_criteria_temperature,
312 sa[0].decreasing_factor, sa[0].monte_carlo_steps), _2OPT());
313 auto _ForkJoin = [](unsigned int n, TSP::transformer_type map_fun){ return Measure(ForkJoin(n,
map_fun)); };
314 auto _DisplayOutput = Display("TSP OUTPUT", DisplayFlags::EmitMathematicaGraphPlot);
315 #pragma endregion
316
317 //TSP -> NN -> Generations( g, ForkJoin ( n, SA -> 2-OPT ) ) -> TSP'
318 auto result = _TSP
319 .map(_DisplayInput)
320 .map(_NN)
321 .map(Generations(g, _ForkJoin(n, _SA_2OPT)))
322 .map(_DisplayOutput);
323 }
324
325 //TSP -> NN -> Generations( g, ForkJoin ( n, GA -> 2-OPT ) ) -> TSP'
326 void Pipeline2(tsp_class& tsp_instance, unsigned int number_of_tasks,
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\program.cpp 6
327 unsigned int number_of_generations)
328 {
329 #pragma region "PipelineConfiguration"
330 auto a = Args<General_args_type>(make_General_args(number_of_generations, number_of_tasks));
331 auto sa = Args<SA_args_type>();
332 auto aco = Args<ACO_args_type>();
333 auto ga = Args<GA_args_type>(make_GA_args(1000, 10, 5, 50000, 10, 0.9));
334
335 const char* pipeline_description = "TSP -> NN -> Generations( g, ForkJoin ( n, GA -> 2-OPT ) ) ->
TSP'";
336 display_args(pipeline_description, a, sa, aco, ga);
337
338 auto g = a[0].number_of_iterations_or_generations;
339 auto n = a[0].number_of_tasks_in_parallel;
340 auto _TSP = TSP(just(tsp_instance));
341 auto _DisplayInput = Display("TSP INPUT", DisplayFlags::All);
342 auto _NN = Measure(NN(), Display("NEAREST NEIGHBOUR", DisplayFlags::EmitMathematicaGraphPlot));
343 auto _GA_2OPT = Chain(GA(ga[0].population_size, ga[0].mutation_percentage, ga[0].group_size,
344 ga[0].number_of_generations, ga[0].nearby_cities, ga[0].
nearby_cities_percentage), _2OPT());
345 auto _ForkJoin = [](unsigned int n, TSP::transformer_type map_fun){ return Measure(ForkJoin(n,
map_fun)); };
346 auto _DisplayOutput = Display("TSP OUTPUT", DisplayFlags::EmitMathematicaGraphPlot);
347 #pragma endregion
348
349 //TSP -> NN -> Generations( g, ForkJoin ( n, GA -> 2-OPT ) ) -> TSP'
350 auto result = _TSP
351 .map(_DisplayInput)
352 .map(_NN)
353 .map(Generations(g, _ForkJoin(n, _GA_2OPT)))
354 .map(_DisplayOutput);
355 }
356
357 //TSP -> NN -> Generations( g, ForkJoin ( n, ACO -> 2-OPT ) ) -> TSP'
358 void Pipeline3(tsp_class& tsp_instance, unsigned int number_of_tasks,
359 unsigned int number_of_generations)
360 {
361 #pragma region "PipelineConfiguration"
362 auto a = Args<General_args_type>(make_General_args(number_of_generations, number_of_tasks));
363 auto sa = Args<SA_args_type>();
364 auto ga = Args<GA_args_type>();
365
366 const int aco_iterations = static_cast<int>(tsp_instance.cities.size() * 100);
367 const ants_type::size_type number_of_ants = tsp_instance.cities.size();
368 const double BASE_PHEROMONE = 1.0f / static_cast<double>(tsp_instance.cities.size());
369 const double ALPHA = 1.0;
370 const double BETA = 1.0;
371 const double RHO = 0.9;
372 const double QVAL = 70;
373 auto aco = Args<ACO_args_type>(make_ACO_args(aco_iterations, number_of_ants,
374 BASE_PHEROMONE, ALPHA, BETA, RHO, QVAL));
375
376 const char* pipeline_description = "TSP -> NN -> Generations( g, ForkJoin ( n, ACO -> 2-OPT ) ) ->
TSP'";
377 display_args(pipeline_description, a, sa, aco, ga);
378
379 auto g = a[0].number_of_iterations_or_generations;
380 auto n = a[0].number_of_tasks_in_parallel;
381 auto _TSP = TSP(just(tsp_instance));
382 auto _DisplayInput = Display("TSP INPUT", DisplayFlags::All);
383 auto _NN = Measure(NN(), Display("NEAREST NEIGHBOUR", DisplayFlags::EmitMathematicaGraphPlot));
384 auto _ACO_2OPT = Chain(ACO(aco[0].aco_iterations, aco[0].number_of_ants, aco[0].base_pheromone,
385 aco[0].favor_pheromone_level_over_distance,
386 aco[0].favor_distance_over_pheromone_level,
387 aco[0].value_for_intensification_and_evaporation,
388 aco[0].pheronome_distribution), _2OPT());
C:\Users\Fabio Galuppo\Documents\Visual Studio 2012\Projects\TSPSolution\TSPSolution\program.cpp 7
389 auto _ForkJoin = [](unsigned int n, TSP::transformer_type map_fun){ return Measure(ForkJoin(n,
map_fun)); };
390 auto _DisplayOutput = Display("TSP OUTPUT", DisplayFlags::EmitMathematicaGraphPlot);
391 #pragma endregion
392
393 //TSP -> NN -> Generations( g, ForkJoin ( n, ACO -> 2-OPT ) ) -> TSP'
394 auto result = _TSP
395 .map(_DisplayInput)
396 .map(_NN)
397 .map(Generations(g, _ForkJoin(n, _ACO_2OPT)))
398 .map(_DisplayOutput);
399 }
400
401 //tsplibreader ..\_tsplib\att48.tsp | tspsolution -p:1 -g:2 -n:4 > tsp_result.txt
402 int main(int argc, char* argv[])
403 {
404 //MonadicLawsProofing();
405
406 unsigned int p = 0, g = 0, n = 0;
407 get_args(argc, argv, p, g, n);
408
409 std::vector<pipeline_type> pipelines;
410 pipelines.push_back(Pipeline1); //TSP -> NN -> Generations( g, ForkJoin ( n, SA -> 2-OPT ) ) ->
TSP'
411 pipelines.push_back(Pipeline2); //TSP -> NN -> Generations( g, ForkJoin ( n, GA -> 2-OPT ) ) ->
TSP'
412 pipelines.push_back(Pipeline3); //TSP -> NN -> Generations( g, ForkJoin ( n, ACO -> 2-OPT ) ) ->
TSP'
413
414 if (p > pipelines.size())
415 {
416 std::cerr << "Invalid pipeline #" << p
417 << ", max # of pipelines = " << pipelines.size() << std::endl;
418 exit(1);
419 }
420
421 auto tsp_instance = read_tsp_instance();
422 pipelines[p - 1](tsp_instance, n, g);
423 }
12/11/13 Curr culo!do!Sistema!de!Curr culos!Lattes!(Fabio!Razzo!Galuppo)

Fabio Razzo Galuppo


Endereço para acessar este C V: http://lattes.cnpq.br/3009012868838154
'ltima atualizaç#o do curr!culo em 01/12/2013

Possui graduaç#o em Ciência da Computaç#o pela Universidade Ibirapuera(2007). Atuando


principalmente nos seguintes temas:Computaç#o Paralela e Concorrente, Metaheur!stica Paralela,
Inteligência Artificial, Otimizaç#o Combinat$ria, Algoritmos e Programaç#o Heterogênea. (Texto gerado
automaticamente pela aplicaç#o CVLattes)

Identificaç#o
Nome Fabio Razzo Galuppo
Nome em citaç!es bibliogr"ficas GALUPPO, F. R.

Endereço

Formaç#o acadêmica/titulaç#o
2012 Mestrado em andamento em Engenharia Elétrica (Conceito CAPES 4).
Universidade Presbiteriana Mackenzie, MACKENZIE, Brasil.
T!tulo: Resoluç"es do Problema do Caixeiro Viajante Aplicando Algoritmos de
Aproximaç#o, Randomizaç#o e Heur!sticas da Inteligência Artificial com
Computaç#o Paralela,Orientador: Nizam Omar.
Palavras-chave: Computaç#o Paralela e Concorrente; Metaheur!stica Paralela;
Inteligência Artificial; Otimizaç#o Combinat$ria; Algoritmos; Programaç#o
Heterogênea.
Grande %rea: Ciências Exatas e da Terra / &rea: Ciência da Computaç#o /
Sub%rea: Inteligência Artificial.
Grande &rea: Ciências Exatas e da Terra / &rea: Matem%tica / Sub%rea:
Otimizaç#o Combinat$ria.
2004 - 2007 Graduaç#o em Ciência da Computaç#o.
Universidade Ibirapuera, UNIB, Brasil.
T!tulo: Abstraç"es para Programaç#o de Sistemas com Multiprocessamento
Simétrico.
Orientador: Floriano Ferreira dos Reis Filho.

Formaç#o Complementar
2013 - 2013 Pattern-Oriented Software Architectures.
Coursera.
2013 - 2013 Coding the Matrix: Linear Algebra through Computer.
Coursera.
2012 - 2012 Heterogeneous Parallel Programming.
Coursera.

buscatextual.cnpq.br/buscatextual/visualizacv.do?id=K4484791D3 1/3
12/11/13 Curr culo!do!Sistema!de!Curr culos!Lattes!(Fabio!Razzo!Galuppo)

2012 - 2012 Functional Programming Principles in Scala.


Coursera.

&reas de atuaç#o

Idiomas
Inglês Compreende Bem, Fala Bem, Lê Bem, Escreve Bem.

Produç"es

Produç#o bibliogr"fica

Livros publicados/organizados ou ediç!es


1. GALUPPO, F. R. . Desenvolvendo com C*. 1. ed. Porto Alegre: Bookman, 2004. v. 1. 496p .

Apresentaç!es de Trabalho
1. GALUPPO, F. R. . C++ 11 e o futuro do C++. 2013. (Apresentaç#o de Trabalho/Conferência ou palestra).

2. GALUPPO, F. R. . Introduç#o ao C++ AMP. 2012. (Apresentaç#o de Trabalho/Conferência ou palestra).

Produç#o técnica

Redes sociais, websites e blogs


1. GALUPPO, F. R. . C++ Renaissance < Functional Revolution. 2011; Tema: Desenvolvimento de Software. (Blog).

Demais tipos de produç#o técnica

Educaç#o e Popularizaç#o de C < T

Redes sociais, websites e blogs

1. GALUPPO, F. R. . C++ Renaissance < Functional Revolution. 2011; Tema: Desenvolvimento de Software. (Blog).

P%gina gerada pelo Sistema Curr!culo Lattes em 11/12/2013 às 10:03:34

buscatextual.cnpq.br/buscatextual/visualizacv.do?id=K4484791D3 2/3

Você também pode gostar