• Nenhum resultado encontrado

Otimiza¸c˜ao por colˆonia de formigas (ACO), bem como o SA e GA descritos anterior-mente, ´e outro m´etodo de otimiza¸c˜ao inspirado na natureza, neste caso, ele foi baseado na observa¸c˜ao do comportamento de formigas reais atuando em colˆonias (CECILIA, 2013).

Mesmo n˜ao sendo as criaturas mais inteligentes deste planeta, todavia sendo um inseto social, as formigas apresentam uma not´avel inteligˆencia coletiva enquanto colˆonia. A inteligˆencia exposta pelas colˆonias de formigas ´e um exemplo de comportamento emer-gente (LUCCI, 2012). Comportamento emeremer-gente significa que um grupo de aemer-gentes ou entidades simples, no caso as formigas, conseguem atuar em um ambiente, formando comportamentos complexos em seu coletivo. O comportamento das formigas ´e gover-nado pela sobrevivˆencia da colˆonia ao inv´es da sobrevivˆencia dos ind´ıviduos, sendo esta caracter´ıstica uma das pe¸cas centrais do algoritmo desta se¸c˜ao.

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¸c˜ao do ACO foi com o PCV, em sua forma original denominada de Ant System (AS) (DORIGO, 1996).

No ACO, em essˆencia, as formigas artificiais ou simuladas geram solu¸c˜oes para instˆancias do PCV em forma de tours ou ciclos. Essas formigas s˜ao simplesmente agentes que possuem a capacidade de construir tours de forma paralela e probabil´ıstica. Elas s˜ao orientadas nesse processo por uma trilha virtual de feromˆonio (pheromone trail) e outras informa¸c˜oes pertinentes a heur´ıstica. Este rastro de feromˆonio ´e um elemento fundamental no algoritmo, ele ´e o facilitador ou estimulador da coordena¸c˜ao indireta entre os agen-tes e o ambiente, onde este processo ´e 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˜ao distribu´ıdas em torno deste grafo. As formigas selecionam, de forma probabil´ıstica, seus caminhos baseados na intensidade de feromˆonio onde as arestas ou conex˜oes do grafo n˜ao estejam ocupadas ou foram visitadas previamente. Quanto maior o n´ıvel de feromˆonio em uma conex˜ao, maior a probabilidade dela ser escolhida.

Quando as formigas completam um ciclo, a fun¸c˜ao de fitness ´e avaliada e o n´ıvel de feromˆonio do grafo ´e atualizado. Ao passar do tempo, as melhores arestas do grafo, que determinar˜ao um ciclo m´ınimo possuir˜ao um n´ıvel de feromˆonio maior, portanto exister´a a convergˆencia, ou seja o maior n´umero de formigas percorrendo o mesmo circuito.

Existem duas a¸c˜oes atuando sob o n´ıvel de feromˆonio depositado no grafo s˜ao elas:

a intesifica¸c˜ao e a evapora¸c˜ao. As a¸c˜oes de intensifica¸c˜ao e evapora¸c˜ao s˜ao regidas pelas seguintes equa¸c˜oes:

Figura 15: Equa¸c˜oes da distribui¸c˜ao, intensifica¸c˜ao e evapora¸c˜ao do feromˆonio.

Outra equa¸c˜ao que faz parte do ACO ´e a de probabilidade para a sele¸c˜ao de caminho, onde o objetivo ´e definir qual ser´a a pr´oxima cidade (ou v´ertice) visitada. Ela ´e obtida atrav´es da rela¸c˜ao a seguir:

Figura 16: Equa¸c˜ao de probabilidade para sele¸c˜ao do caminho.

A fun¸c˜ao ANT-COLONY-OPTIMIZATION apresenta em pseudoc´odigo o m´etodo do ACO descrito anteriormente:

Figura 17: Fun¸c˜ao ANT-COLONY-OPTIMIZATION.

3.4 Resolu¸ c˜ ao Heur´ıstica por Busca Local 2-OPT

A heur´ıstica de busca local 2-Opt para resolu¸c˜ao do PCV (CROES, 1958) serve para ajustar um circuito de uma instˆancia do problema. Esta heur´ıstica busca o melhor resul-tado local, eliminando os cruzamentos entre as arestas. Percorrendo todos os pares das arestas n˜ao adjacentes e alterando suas conex˜oes ´e poss´ıvel alcan¸car o m´ınimo local do circuito, conforme descrito nos passos do algoritmo em pseudoc´odigo a seguir:

Figura 18: Fun¸c˜oes 2-OPT-SWAP e 2-OPT-ALL. Elas comp˜oem a heur´ıstica 2-Opt.

4 DESEMPENHO E ALGORITMOS PARALELOS

A preocupa¸c˜ao, ou o questionamento sobre o desempenho computacional em rela¸c˜ao ao tempo, ocorre desde o s´eculo XIX, quando foi concebida a M´aquina An´alitica 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´alise matem´atica de algoritmos ´e fundamentado e baseado nas opera¸c˜oes sequenciais, onde as instru¸c˜oes n˜ao podem ocorrer no mesmo tempo. Este mo-delo ´e conhecido como arquitetura de von Neumann ou arquitetura de Princeton (NEU-MANN, 1993).

A Arquitetura de von Neumann ainda ´e a arquitetura dominante na constru¸c˜ao de computadores convencionais. Na constru¸c˜ao de um algoritmo, existem duas propriedades fundamentais a serem consideradas e medidas: tempo de processamento e espa¸co dos dados. Uma vez determinado o espa¸co consumido por um algoritmo sequencial, isto torna-se uma caracter´ıstica imut´avel, sua varia¸c˜ao de tempo de processamento ser´a proporcional a sua complexidade e a velocidade do processador. No entanto, existe a possibilidade da constru¸c˜ao de algoritmos usando t´ecnicas de computa¸c˜ao paralela para aumentar o desempenho em rela¸c˜ao ao tempo de processamento e, consequentemente em muitos casos, consumindo mais espa¸co de dados (embora em um intervalo menor de tempo).

Atualmente, para executar mais computa¸c˜oes por segundo, os processadores est˜ao sendo desenvolvidos com mais de um n´ucleo de processamento, denominados de processa-dores multicore, em outras palavras eles s˜ao um tipo de computador paralelo. Portanto, para obter o melhor desempenho destes computadores, ser´a necess´ario explorar algoritmos usando t´ecnicas de computa¸c˜ao 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ˆencia da constru¸c˜ao destes novos hardwares (processadores com capacidade de computa¸c˜ao paralela). Onde computadores com quatro

n´ucleos s˜ao comuns hoje em dia. Para tomar vantagem destes tipos de processadores, novos algoritmos e t´ecnicas de programa¸c˜ao s˜ao necess´arias. Uma das t´ecnicas populares

´e amap-reduce, inspirada pela programa¸c˜ao funcional (HAREL, 2012a). Onde o objetivo

´e dividir, conquistar e muitas vezes consolidar, isto ´e tamb´em conhecido como fork-join ou divide-conquer. Por exemplo, o MERGE-SORT (CORMEN, 2009), ´e estruturado adequadamente para a aplica¸c˜ao desta t´ecnica:

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

A t´ecnica de map-reduce, determina que a computa¸c˜ao de um grande agrupamento de dados seja dividida em partes, onde estas partes s˜ao executadas separadamente e os resultados s˜ao combinados com a utiliza¸c˜ao de uma fun¸c˜ao de acumula¸c˜ao (HAREL, 2012a). No caso do MERGE-SORT, a fun¸c˜ao recursivamente efetua a quebra do conjunto de dados em partes que s˜ao executadas independentes, e a fun¸c˜ao MERGE tem por objetivo consolidar o processamento das etapas previamente divididas. Este algoritmo na vers˜ao sequencial implica, no pior caso, em uma complexidade de tempo igual a O(N LOG N), e sua vers˜ao paralela a O(N) (HAREL, 2012a). Em contrapartida, vers˜ao paralela do MERGE-SORT, exige um consumor maior de mem´oria.

Problemas intrat´aveis, como no caso do PCV, podem se beneficiar de uma classe de heur´ıstica, ao qual coletivamente ´e chamada de metaheur´ıstica paralela. Ela inclui os tradicionais algoritmos de metaheur´ıstica, como por exemplo,Simulated Annealing (SA), Algoritmo Gen´etico (GA), Otimiza¸c˜ao por Colˆonia de Formigas (ACO), entre outros.

Portanto, seu objetivo ´e a composi¸c˜ao destes algoritmos com t´ecnicas de computa¸c˜ao pa-ralela para reduzir o tempo de processamento e utilizar os diversos tipos de processadores (CPUs e/ou GPUs) no m´aximo de suas capacidades.

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

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

No caso do SA em paralelo fica evidente que o n´umero de solu¸c˜oes candidatas au-mentar˜ao proporcionalmente ao n´umero de entidades paralelas disponibilizadas para a resolu¸c˜ao do problema, permitindo tais solu¸c˜oes rodarem desde smartphones at´e super-computadores. Ent˜ao, tamb´em ´e necess´ario identificar qual a rela¸c˜ao entre a quantidade de entidades paralelas e a qualidade do resultado. Intuitivamente, ´e poss´ıvel interpretar quanto maior for o paralelismo dispon´ıvel, melhor ser´a a qualidade do resultado. Ou seja, um comportamento, em teoria, monotonicamente decrescente, j´a que o PCV ´e um problema de minimiza¸c˜ao. No entanto, para esta conclus˜ao ´e necess´aria a verifica¸c˜ao e os testes em detalhes dos algoritmos propostos nesta obra em quantidades diversas de aloca¸c˜oes de processamento e tamanho do problema.

Outro modelo que est´a presente nos computadores atuais e deve ser considerado, ´e o processamento vetorial. Este tipo de processamento est´a presente nos processadores con-vencionais e processadores paralelos massivos, tais como, aceleradoras gr´aficas discretas ou Graphical Processing Units (GPUs). A computa¸c˜ao processada por este tipo de dis-positivo ´e classificada atrav´es da taxonomia de Flynn como Single Instruction, Multiple Data (SIMD) ouMultiple Instruction, Multiple Data (MIMD) (AKHTER, 2006).

A busca, a utiliza¸c˜ao e a cria¸c˜ao do paralelismo ´e um dos maiores assuntos de pes-quisa desde do in´ıcio da d´ecada de 80. Onde a for¸ca motivadora para estas quest˜oes de paralelismo, em hardware e software, ´e a necessidade insaci´avel por maior desempenho, especialmente por uma alta velocidade computacional (TOSIC, 2004).

O objetivo desta obra ´e abordar o PCV por meio da composi¸c˜ao dos algoritmos de busca da Inteligˆencia Artificial, da aplica¸c˜ao de paralelismo e da utiliza¸c˜ao de recursos computacionais de alto-desempenho. Mapeando quais s˜ao as vantagens, ganhos e desafios com rela¸c˜ao a constru¸c˜ao de algoritmos sequenciais. As abordagens aqui apresentadas per-mitir˜ao serem adaptadas ou servirem de base para resolu¸c˜ao de outros tipos de problemas intrat´aveis.

4.1 Fork/Join para distribuir, explorar e selecionar

Em geral, a aplica¸c˜ao do padr˜aoFork/Jointem como caracter´ıstica a cria¸c˜ao dinˆamica de tarefas (fork) com a finalidade de resolver um problema e mais adiante a jun¸c˜ao destas tarefas (join) para dar continuidade na execu¸c˜ao. Ele ´e muito comum de ser encontrado em conjunto com outro padr˜ao denominado Loop Parallelism (MATTSON, 2004). Padr˜oes como OpenMP ou ISO C++ 11 oferecem APIs ao qual a finalidade ´e a espalhar tarefas computacionais para escalar o processamento. No OpenMP, em C++, isto ´e feito atrav´es de diretivas (#pragma omp parallel for) que delega ao compilador o trabalho da gera¸c˜ao do paralelismo e sincroniza¸c˜ao do ponto de jun¸c˜ao.

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

Usualmente, o Fork/Join ´e aplicado em situa¸c˜oes de decomposi¸c˜ao de tarefas ou de dados (MATTSON, 2004), por exemplo, ao dividir tarefas para atuar em parti¸c˜oes dos dados e ao final consolidar. Em alguns casos particionando o espa¸co de estados a ser explorado. De forma abstrata este padr˜ao oferece o seguinte comportamento:

Figura 21: Modelo abstrato do Fork/Join.

No ISO C++ 11 (ISOCPP, 2011), a linguagem de programa¸c˜ao adotada na imple-menta¸c˜ao desta obra, ´e permitido um ajuste mais customiz´avel e moderno na express˜ao do paralelismo e da sincroniza¸c˜ao, isto ´e feito com uma combina¸c˜ao entre std::vector, std::async estd::future, como ilustrado no fragmento a seguir:

Figura 22: Fragmento em C++ 11 ilustrativo a aplica¸c˜ao de std::vector, std::async e std::future do functor ForkJoin da implementa¸c˜ao proposta (detalhes no anexo).

A aplica¸c˜ao do padr˜ao Fork/Join nesta obra objetiva a cria¸c˜ao de diversas tarefas explorando (preferencialmente) o maior n´umero de estados distintos do espa¸co de esta-dos de uma instˆancia do PCV. Este trabalho ocorre em conjunto com os algoritmos de inteligˆencia artificial selecionados e as estrat´egias descritas no decorrer desta obra.

5 MODELO COMPOSICIONAL PARA RESOLUC ¸ ˜ AO DO PCV

Como descrito nos cap´ıtulos 3 e 4 sobre as t´ecnicas selecionadas nesta obra relativas a resolu¸c˜ao de sistemas complexos e aos padr˜oes de computa¸c˜ao paralela. Aqui ser´a apresentado o modelo proposto de resolu¸c˜ao do PCV atrav´es de composi¸c˜ao. A figura 23 denota de forma geral a id´eia composicional proposta para abordagem do problema:

Figura 23: Composi¸c˜ao entre algoritmos da Inteligˆencia Artificial e Computa¸c˜ao Paralela.

A composi¸c˜ao aplicada aqui possui um relacionamento indireto com o conceito ma-tem´atico de composi¸c˜ao de fun¸c˜oes. A inspira¸c˜ao `a aplica¸c˜ao de um modelo composi-cional vem das Monads, baseadas em conceitos da Teoria das Categorias e introduzidas na d´ecada de 90 na linguagem de programa¸c˜ao funcional Haskell (WADLER, 1992)(WA-DLER, 1995).

Umamonad´e uma forma de estrutura computacional em termos de valores e sequˆencias de computa¸c˜ao usando estes valores. Ela permite a defini¸c˜ao de building blocks que s˜ao sequˆencias de computa¸c˜ao, al´em disso ela determina como combinar computa¸c˜oes e formar uma nova, esse encadeamento ´e a composi¸c˜ao, onde a sa´ıda de uma computa¸c˜ao ´e a en-trada de uma outra. Este processo, teoricamente, ocorre de forma indefinida, ou seja, cabe ao implementador saber quando ´e o suficiente utilizar o valor retornado como resultado, encerrando a sequˆencia computacional. Com uma monad ´e permitido criar um pipeline de execu¸c˜ao. No caso do PCV, a solu¸c˜ao proposta envolve uma monad que permitir´a a utiliza¸c˜ao de building blocks contextuais, aos quais sabem atuar sobre as instˆancias do PCV, onde habilitar´a a constru¸c˜ao de um ou mais pipelines para resolver o problema.

De acordo com as defini¸c˜oes pertinentes da Teoria das Categorias, a proposta inclui uma cole¸c˜ao de objetos do tipo PCV e uma cole¸c˜ao de morfismos para satisfazer o mo-delo composicional. O momo-delo de morfismo proposto ´e uma fun¸c˜ao, no caso um estrutura computacional pr´e-definida, a qual sabe manipular, transformar ou atuar em um dom´ınio

(PCV) e produzir um contradom´ınio (PCV). Em uma categoria esse tipo de morfismo re-cebe o nome de endomorfismo. Esse endomorfismo poder´a ser concretizado, por exemplo, atrav´es da aplica¸c˜ao de uma fun¸c˜ao representando um algoritmo de SA. Ela receber´a uma instˆancia do PCV com uma determinada configura¸c˜ao e produzir´a um objeto destino, de preferˆencia, igual ou melhor ao objeto de origem.

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

Figura 24: Morfismo com objetos mon´adicos [M(TSP)].

A sequˆencia a seguir ´e a nota¸c˜ao de uma composi¸c˜ao de objetos onde o objetivo ´e estabelecer umpipeline para solu¸c˜ao de uma instˆancia do PCV. A opera¸c˜ao de composi¸c˜ao satisfaz `a propriedade associativa:

Figura 25: Composi¸c˜ao com objetos mon´adicos [M(TSP)].

O tipo param´etrico Monad em Haskell possui uma interface contendo trˆes membros s˜ao eles: um construtor de tipo, uma opera¸c˜ao para binding e uma fun¸c˜ao de unidade. O construtor de tipo serve para obter ou instanciar o tipo mon´adico. A fun¸c˜ao de unidade mapeia um valor simples para um valor do tipo mon´adico correspondente, no caso do PCV, o que ´e deseja ´e um tipo PCV mon´adico, portanto uma estrutura de dados do tipo PCV sofre uma aplica¸c˜ao de uma fun¸c˜ao de unidade para ser transformado em um tipo PCV

mon´adico. A opera¸c˜ao debinding´e respons´avel por mapear um valor mon´adico em outro, onde poder´a possuir o mesmo tipo do objeto de origem. Na figura 26 ´e apresentada a classe Monad do Haskell e seus membros (HUGHES, 1998). ´E poss´ıvel interpret´a-la como uma abstra¸c˜ao que encapsula um tipo e sua computa¸c˜ao:

Figura 26: Monad, um tipo de dados abstrato de a¸c˜oes.

Na figura 26, o membro return ´e a fun¸c˜ao de unidade e o operador>>= representa a opera¸c˜ao de binding. O construtor de tipo ´e a pr´opria Monad instanciada, por exemplo, M TSP.

Para um tipo ser qualificado como monad torna-se essencial que trˆes axiomas sejam satisfeitos: o axioma da associatividade, o axioma da identidade `a esquerda ou aplica¸c˜ao da fun¸c˜ao de unidade `a esquerda e o axioma da identidade `a direita ou aplica¸c˜ao da fun¸c˜ao de unidade `a direita. Estes axiomas s˜ao conhecidos como as leis da monad (WADLER, 1995) e est˜ao formalizados na nota¸c˜ao do c´alculo lambda (λ calculus) como segue:

Figura 27: As leis da Monad (Adapta¸c˜ao da formaliza¸c˜ao de Wadler (WADLER, 1995)).

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

como por exemplo IO Monad que sabe lidar com computa¸c˜oes que envolvem opera¸c˜oes E/S ouError Monad que sabe lidar com computa¸c˜oes que envolvem opera¸c˜oes as quais po-dem falhar ou disparar uma exce¸c˜ao. O conceito demonad em linguagens de programa¸c˜ao

n˜ao ´e 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¸c˜oes 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¸c˜ao C++, s˜ao apresentados a seguir:

Figura 28: TSP Monad.

A TSP Monad proposta na figura 28 fornece um construtor de tipo, uma fun¸c˜ao de unidade a partir da fun¸c˜ao ret e uma opera¸c˜ao de binding a partir da fun¸c˜ao bnd.

Para evitar uma sintaxe convoluta, o m´etodo map foi adicionado - esta abordagem ´e semelhante a constru¸c˜ao da fun¸c˜ao flatMap da linguagem Scala (SCALA-LANG.ORG, 2013). Para fazer parte da categoria das monads ´e fundamental esse tipo obedecer as trˆes leis referenciadas na figura 27. As provas relativas as leis citadas s˜ao apresentadas a seguir. Elas s˜ao apresentadas em duas vers˜oes, uma utilizando um nota¸c˜ao funcional e outra uma nota¸c˜ao orientada a objetos:

Figura 29: TSP Monad e a primeira lei.

Figura 30: TSP Monad e a segunda lei.

Figura 31: TSP Monad e a terceira lei.

Portanto, conforme demonstrado, a TSP Monad (figura 28) ´e uma autˆenticamonad! Amonad proposta (figura 28) permitir´a constru¸c˜oes depipelines computacionais para resolu¸c˜ao do PCV conforme vislumbrado na figura 23, ao qual poder˜ao encadear uma sequˆencia de opera¸c˜oes. Um comportamento similar ao padr˜ao de projeto denominado Cadeia de Responsabilidade (Chain-of-Responsibility pattern) (GAMMA, 1995)(RAJAN, 2010). No entanto, o padr˜ao descrito atrav´es da orienta¸c˜ao a objetos ´e verboso se for comparado com o modelo mon´adico sugerido.

O m´etodo map ou a fun¸c˜ao bnd da TSP Monad s˜ao os pontos de entrada para os morfismos, ou seja, s˜ao membros que recebem fun¸c˜oes transformadoras - elas manipulam uma instˆancia da estrutura TSP e geram um tipo mon´adico TSP. Por exemplo, a cons-tru¸c˜ao de um pipeline simples contendo o encadeamento do Simulated Annealing (SA) e

do2-OPT ´e descrito na figura 32 abaixo:

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

Se aplicado a uma instˆancia do PCV, o pipeline indicado na figura 32, ap´os

Se aplicado a uma instˆancia do PCV, o pipeline indicado na figura 32, ap´os

Documentos relacionados