Síntese de Programas via Localizador de Modelo
Universidade Federal de Pernambuco [email protected] http://cin.ufpe.br/~posgraduacao
Recife 2019
Síntese de Programas via Localizador de Modelo
Tese apresentada ao Programa de Pós-Graduação em Ciências da Computação da Universidade Federal de Pernambuco, como requisito parcial para a obtenção do título de Doutor em Ciências da Computação. Área de Concentração: Engenharia de Software e Linguagens de Programação. Orientador: Prof. Dr. Juliano Manbu Iyoda
Coorientador: Prof. Dr. Alexandre Cabral Mota.
Recife 2019
Catalogação na fonte
Bibliotecária Monick Raquel Silvestre da S. Portes, CRB4-1217
C824s Correia, Alexandre Roberto de Souza
Síntese de programas via localizador de modelo / Alexandre Roberto de Souza Correia. – 2019.
140 f.: il., fig., tab.
Orientador: Juliano Manabu Iyoda.
Tese (Doutorado) – Universidade Federal de Pernambuco. CIn, Ciência da Computação, Recife, 2019.
Inclui referências e apêndice.
1. Engenharia de software. 2. Síntese de programas. 3. Linguagem de programação. I. Iyoda, Juliano Manabu (orientador). II. Título.
005.1 CDD (23. ed.) UFPE - CCEN 2020 - 63
“Síntese de Programas via Localizador de Modelo”
Tese de Doutorado apresentada ao Programa
de Pós-Graduação em Ciência da
Computação da Universidade Federal de
Pernambuco, como requisito parcial para a
obtenção do título de Doutor em Ciência da
Computação.
Aprovado em: 24/01/2020.
________________________________________
Orientador: Prof. Dr. Juliano Manabu IyodaBANCA EXAMINADORA
__________________________________________________________
Prof. Dr. Ricardo Bastos Cavalcante Prudêncio
Centro de Informática / UFPE
__________________________________________________________
Prof. Dr. Márcio Lopes Cornélio
Centro de Informática / UFPE
__________________________________________________________
Prof. Dr. Gustavo Araújo Soares
Microsoft Corporation
__________________________________________________________
Prof. Dr. Haniel Moreira Barbosa
Departamento de Ciência da Computação/UFMG
__________________________________________________________
Prof. Dr. Tiago Lima Massoni
alunos e todas as pessoas que acreditam, apoiam e estão (ou estiveram) disponíveis para ajudar a melhorar a condição de vida das pessoas pela educação.
Os resultados apresentados até aqui foram obtidos com o apoio de muitas pessoas. Sem elas eu não teria conseguido, por isso, gostaria inicialmente de dizer que sinto-me honrado por tê-los por perto e que me esforço continuamente para honrar cada um de vocês. Por fim, deixo o meu profundo agradecimento:
À Jesus Cristo porque ELE me ama, me guia, me perdoa e me abençoa, mesmo sabendo que eu não mereço ou da minha dificuldade em melhorar.
À minha esposa Nadja Correia, por tanta coisa que vou tentar resumir como: aqui na terra Nadja tem sido minha fortaleza de paciência, encorajamento e companheirismo.
Aos meus filhos Pedro, Joana e Hugo Correia, pela oportunidade de convívio e por tudo que vocês três têm me ensinado.
Aos meus pais Ahilson e Leone Correia e meus padrinhos Gualberto e Nina Almeida (estendendo aos meus familiares) pelas lições de vida, apoio incondicional e oportunidades de seguir aprendendo.
Aos meus professores orientadores Juliano Iyoda e Alexandre Mota (estendendo à co-munidade de amigos acadêmicos da UFPE) pela oportunidade, conhecimento, orientação, apoio e ambiente de aprendizagem favorável durante toda essa jornada.
Aos pastores Marcos Amazonas (estendendo aos membros da 1ª Igreja Baptista de Coimbra/Portugal) e Altemar Ribeiro (estendendo aos membros da 1ª Igreja Evangélica Congregacional de Juazeiro da Bahia/Brasil) pelos ensinamentos do que é ser servo de Deus e pelos planos DELE para as nossas vidas.
À professora Leopoldina Veras (estendendo aos amigos do Instituto Federal do Sertão Pernambucano) pela oportunidade de transformar vidas através da educação, numa região exposta à grandes dificuldades.
Aos meus alunos, no qual, a partir deles (assim como as novas gerações de indivíduos em computação evolucionária) vejo ser renovada a esperança de tempos transformadores a partir da educação.
Uma das finalidades de Síntese de programas é mecanizar a tarefa de programar, a partir da intenção do usuário (expressa de diferentes formas como pré/pós condição, exemplos, sketches, etc). Há diversas abordagens de síntese de programas que costumam ser implementadas isoladamente: dedutiva, guiada por sintaxe, indutiva, etc. Neste traba-lho, descrevemos o PSMF, como uma abordagem (implementada como um sintetizador) que combina modelo de busca e algoritmo genético. O PSMF proposto expressa a in-tenção do usuário em exemplos de entrada/saída (exemplos E/S), soft sketch (ou seja, um conjunto de comandos que devem aparecer no programa sintetizado, mas são escritos em qualquer ordem) e ingredientes sintáticos (quantidades de construtores sintáticos que devem aparecer no programa sintetizado). A saída gerada pelo PSMF é um programa imperativo de propósito geral. A combinação de síntese indutiva com algoritmo genético permitiu o PSMF sintetizar programas com (ou sem) arrays, totalizando 16 programas (Max2, Max3, Max4, GCD, IntSqrt, Maj5, Maj8, Modu, Fact, Fib, aMax; aDouble; aSum, eCount, aBubSort e aSelSort) bem conhecidos nas comunidades de síntese de programas (Competição SyGuS, Programação Genética). Executamos avaliações empíricas da efeti-vidade da nova implementação e o tempo médio de síntese desses 16 programas variou de 5.0 segundos (Max2) até 15.9 minutos (Fib).
Palavras-chaves: Síntese de programas. Alloy*. Algoritmo genético. Programação gené-tica. Linguagens de programação.
One purpose of Program Synthesis is to mechanize the task of programming from the user intent (expressed in various forms like pre/post conditions, examples, sketches, etc). There are many approaches to program synthesis that are usually implemented in isolation: deductive, syntax-based, inductive, etc. In this manuscript, we describe PSMF, a program synthesizer that combines model finder and genetic algorithm. The PSMF proposed takes as user intent examples, and a soft sketch: a new kind of user intent defined as a set of commands that must appear in the synthesized program (and that are in no particular order of execution) and syntactical ingredients (that is the amount of the syntactical constructs whose will have appear in the synthesized program). The output of PSMF is a general purpose imperative program. The combination of inductive synthesis and genetic algorithm has allowed PSMF to synthesize programs with (and with no) arrays, reaching the amount of 16 programs (Max2, Max3, Max4, GCD, IntSqrt, Maj5, Maj8, Modu, Fact, Fib, aMax; aDouble; aSum, eCount, aBubSort e aSelSort) found in the program synthesis communities (such as SyGuS competition, Genetic Programming). We carried out an empirical evaluation of the new PSMF implementation’s effectiveness and the synthesis mean time of these 16 programs have varied from 5.0 seconds (Max2) to 15.9 minutes (Fib).
Keywords: Program synthesis. Alloy*. Genetic algorithm. Genetic programming. Pro-gramming languages.
Figura 1 – Um contraexemplo da propriedade do modelo. . . 36
Figura 2 – Cenário inicial: abordagem dedutiva. . . 46
Figura 3 – Cenário proposto para o PSMF. . . 51
Figura 4 – Visão evolucionária do PSMF. . . 52
Figura 5 – Algumas classes e métodos da biblioteca JGAP usados no PSMF. . . . 55
Figura 6 – O problema da convergência na síntese do Max3. . . 68
Figura 7 – As métricas escolhidas (CB L1, Euc L2, Hellinger e Cheb) representam uma diferença semântica que percorre todas as métricas estudadas (eixo vertical). . . 82
Figura 8 – Interpretação 3D hipotética da formulação de Minkowski. . . 84
Figura 9 – Programa e prova gerados na síntese por prova teórica. . . 95
Figura 10 – Glossário visual dos termos relacionados ao registro de execução. . . 105
Figura 11 – Trabalhos relacionados em função de entradas e saídas. . . 110
Figura 12 – Relação da sintaxe (ldrn) e semântica (f) dos programas candidatos do Max4. . . 114
Figura 13 – Glossário visual dos termos relacionados ao registro de execução. . . 124
Figura 14 – Fluxo de trabalho da PANGEA. . . 126
Figura 15 – Fluxo PANGEA e BCR de síntese de programa comportamental. . . . 126
Figura 16 – AST de um programa candidato p. . . 129
Figura 17 – Cálculo dos atributos (feature) de um programa candidato p. . . 132
Figura 18 – Realizando poda (pruning) na Árvore de decisão de um programa can-didato p. . . 132
Figura 19 – Subprogramas de um programa candidato. . . 133
Figura 20 – AST de outro programa candidato. . . 134
Figura 21 – Cálculo dos atributos (feature) de outro programa candidato p. . . 135
Figura 22 – Subprogramas de outro programa candidato. . . 136
Tabela 2 – PSMF incial: entradas e programa sintetizado . . . 59
Tabela 3 – PSMF proposto: entradas e programa sintetizado . . . 60
Tabela 4 – PSMF proposto: valores das assinaturas para o sucesso da tarefa de síntese. . . 62
Tabela 5 – PSMF proposto: parâmetros para o sucesso da tarefa de síntese. . . 62
Tabela 6 – PSMF proposto: Cromossomo feliz para o sucesso da tarefa de síntese. 65 Tabela 7 – PSMF proposto: Tempos de execução (em segundos) na síntese dos programas. . . 66
Tabela 8 – Entradas e programas sintetizado usando PSMF com arrays. Para sim-plificar a exibição, considere j=IONum[InLocJ], i=IONum[InLocI], e m=IONum[InLocMin]. . . 72
Tabela 9 – Tempo de execução (segundos) da tarefa de síntese. . . 73
Tabela 10 – Efetividade da técnica de algoritmo genético . . . 76
Tabela 11 – PSMF proposto entradas e programa sintetizado (continuação) . . . . 77
Tabela 12 – Ingredientes sintáticos dos programas GCD e maior entre 4 números (Max4) definidos manualmente (células em branco) e mecanicamente por sketch ou automático (células em cinza). . . 85
Tabela 13 – PSMF entradas e programa sintetizado . . . 86
Tabela 14 – PSMF: Delineamento experimental. . . 86
Tabela 15 – PSMF: tempos de síntese (medidos em segundos) do GCD e Max4. . . 88
Tabela 16 – Um breve posicionamento do PSMF na área de síntese de programas. . 109
Tabela 17 – Parâmetros de sequência genética para gerar as expressões de sketch. . 123
Tabela 18 – Suíte de testes de um programa candidato. . . 129
Tabela 19 – Registro de execução de um programa candidato. . . 130
Tabela 20 – ML dataset D de um programa candidato. . . 130
Tabela 21 – Suíte de testes de outro programa candidato. . . 134
Tabela 22 – Registro de execução de outro programa candidato. . . 135
Tabela 23 – ML dataset D de outro programa candidato. . . 135
Tabela 24 – Parâmetros de síntese comportamental de dois programas candidatos 𝑝 e 𝑝#. . . . 137
2.1 Sistema de arquivos. . . 34
2.2 Verificando algumas propriedades do modelo de um sistema de arquivos. . 35
2.3 Propriedade do modelo onde foi encontrado um contraexemplo. . . 35
2.4 Assinaturas hipotéticas para explicar o efeito do comando run no Alloy*. . 37
2.5 Pré-condição do programa Max3. . . 39
2.6 Pós-condição do programa Max3. . . 39
2.7 sketch do programa Max3. . . 40
2.8 Código-fonte desejado do programa Max3 com sketch em negrito. . . 40
2.9 Um programa candidato Max3 com viés de cobertura. . . 42
2.10 Código-fonte alternativo gerado na síntese do Max3. . . 42
3.1 Mudança no estado para aceitar exemplos E/S no PSMF. . . 47
3.2 Predicado de síntese do PSMF. . . 47
3.3 exemplos E/S do programa Max3. . . 48
3.4 Array como assinatura abstrata de expressão aritmética. . . 49
3.5 Redefinição da assinatura Assign para usar arrays. . . 49
3.6 Definição do container do conteúdo do array. . . 49
3.7 Redefinindo a assinatura estado para associar o nome ao container dos arrays. . . 49
3.8 Associando arrays a alguma expressão. . . 50
3.9 Redefinindo o predicado de avaliação para considerar arrays. . . 50
3.10 Atualização de arrays para os estados do sistema. . . 50
3.11 pc . . . 53
3.12 pc’ . . . 53
3.13 pc” . . . 53
4.1 aMax: exemplos E/S em Alloy*. . . 70
4.2 PSMF com Arrays: exemplo de suíte de testes. . . 70
6.1 Implementação recursiva do programa Max por prova de teorema. . . 93
6.2 Sketch para síntese do Max3 em linguagem SKETCH. . . 93
6.3 Resultado da síntese do Max3 em linguagem SKETCH. . . 94
6.4 Implementação recursiva com sketch do programa aMax para listas. . . 96
6.5 Modelagem em Rosette/Racket do lmax (aqui chamado defind_max). . . . 97
6.6 Fatoração de polinômio usando buraco sintático do tipo inteiro. . . 98
6.7 Resultado da síntese do find_max em Rosette/Racket. . . 99
6.8 Sketch em ImpSynt para síntese do find_max em Síntese Natural. . . 100
6.9 Resultado da síntese do find_max em Síntese Natural. . . 100
6.12 Registro de execução. . . 105
A.1 Registro de execução. . . 124
A.2 Código-fonte de um Programa candidato Max3. . . 128
A.3 Outro programa candidato Max3. . . 134
aBubSort ordenação dos elementos do array com Bubble sort
aDouble preenche todos os elementos de um array da seguinte forma: myArray[index] = 2 * index
aMax máximo elemento encontrado em um array
aSelSort ordenação dos elementos do array com Selection sort aSum soma todos os elementos de um array
CEGIS Síntese Indutiva Guiada por Contraexemplo ou Counter Example
Guided Inductive Synthesis
DSL Linguagem de Domínio Específco ou Domain-Specifc Language eCount contagem do número de ocorrências de um valor em um array Fact fatorial de um número
Fib sequência de Fibonacci
GCD máximo divisor comum ou Greatest Common Divisor
IntSqrt maior inteiro menor ou igual à raiz quadrada de um inteiro positivo ou
Integer Square Root
Maj3 maioria de 3 valores ou Majority3 Maj5 maioria de 5 valores ou Majority5 Maj8 maioria de 8 valores ou Majority8 Max2 maior entre 2 números
Max3 maior entre 3 números Max4 maior entre 4 números
Modu resto da divisão entre 2 números
PBE programação por exemplo ou Programming By Example SAT satisfabilidade ou Satisfability
SMT satisfatibilidade módulo teoria ou Satisfability Modulo Theory Swap2 troca de valor entre duas variáveis
1 INTRODUÇÃO . . . 16
1.1 JUSTIFICATIVA . . . 16
1.2 MOTIVAÇÃO . . . 18
1.3 CONTRIBUIÇÕES . . . 19
1.4 ESTRUTURA DESTE DOCUMENTO . . . 20
2 FUNDAMENTAÇÃO TEÓRICA . . . 23
2.1 SÍNTESE DE PROGRAMAS . . . 23
2.1.1 Paradigmas de síntese de programas . . . 25
2.1.2 Desafios em síntese de programas . . . 26
2.2 APRENDIZAGEM DE MÁQUINA . . . 28
2.2.1 Classificação por aprendizagem indutiva . . . 30
2.3 ALGORITMOS GENÉTICOS E PROGRAMAÇÃO GENÉTICA . . . 30
2.3.1 Programação genética, operadores de seleção e busca . . . 31
2.3.2 Dificuldades em algoritmo e programação genética . . . 32
2.4 ABSTRAÇÃO, MODELOS E LINGUAGEM ALLOY . . . 33
2.5 SINTETIZADOR IMP IMPLEMENTADO EM ALLOY* . . . 36
2.5.1 Comando run do sintetizador . . . 37
2.5.2 Um exemplo de uso do sintetizador PSMF inicial . . . 38
2.5.3 Dificuldades de uso do sintetizador . . . 40
2.6 CONSIDERAÇÕES FINAIS . . . 42
3 SINTETIZADOR ALLOY* E ALGORITMO GENÉTICO . . . 44
3.1 CARACTERIZAÇÃO DO PROBLEMA E OBJETIVO . . . 44
3.2 CONTRIBUIÇÕES . . . 45
3.3 CENÁRIO INICIAL: ABORDAGEM DEDUTIVA . . . 46
3.4 MUDANÇA DE ABORDAGEM: DE DEDUTIVA PARA INDUTIVA . . . 47
3.5 IMPLEMENTANDO ARRAYS . . . . 48
3.5.1 Estendendo a sintaxe Alloy* do PSMF . . . 48
3.5.2 Estendendo a semântica Alloy* do PSMF . . . 49
3.6 COMBINANDO ALGORITMO GENÉTICO E LOCALIZADOR DE MODELO 50 3.6.1 Visão evolucionária do PSMF . . . 52
3.6.2 Mutação de programas candidatos com o método next() . . . 52
3.6.3 Implementação do PSMF proposto . . . 54
3.6.4 Funcionamento de algoritmo genético no JGAP . . . 54
4 EXPERIMENTOS E AVALIAÇÃO . . . 58
4.1 CONTRIBUIÇÕES . . . 58
4.2 EXPERIMENTOS: SÍNTESE DE PROGRAMAS SEM ARRAY . . . . 58
4.2.1 Sintonizando os parâmetros do PSMF proposto . . . 60
4.2.2 Desempenho do PSMF proposto . . . 66
4.2.3 Discussão dos resultados . . . 67
4.3 EXPERIMENTOS: SÍNTESE DE PROGRAMAS COM ARRAYS . . . . 69
4.3.1 aMax: Exemplo de síntese com array . . . 70
4.3.2 Discussão dos resultados . . . 74
4.4 EFETIVIDADE DO ALGORITMO GENÉTICO NO PSMF PROPOSTO . . 75
4.5 CONSIDERAÇÕES FINAIS . . . 76
5 PSMF E OUTRAS FITNESS . . . 78
5.1 CARACTERIZAÇÃO DO PROBLEMA E OBJETIVO . . . 78
5.2 CONTRIBUIÇÕES . . . 79
5.3 SISTEMA PROPOSTO . . . 79
5.3.1 Escolha das distâncias . . . 81
5.4 EXPERIMENTOS E RESULTADOS . . . 84
5.4.1 Desempenho do sistema . . . 87
5.5 DISCUSSÃO DOS RESULTADOS . . . 88
5.6 CONSIDERAÇÕES FINAIS . . . 90
6 TRABALHOS RELACIONADOS . . . 91
6.1 PIONEIROS NA TAREFA DE SÍNTESE DE PROGRAMAS . . . 92
6.2 SÍNTESE DE PROGRAMAS E SKETCH . . . 93
6.3 SÍNTESE DE PROGRAMAS POR RESOLVEDORES SAT/SMT . . . 94
6.4 SÍNTESE DE PROGRAMAS COM LINGUAGEM AUXILIADA POR RE-SOLVEDORES E ROSETTE . . . 96
6.5 SÍNTESE NATURAL E IMPSYNT . . . 99
6.6 LÓGICA DA SEPARAÇÃO SINTÉTICA E SUSLIK . . . 101
6.7 PROGRAMAÇÃO POR EXEMPLO E FLASHMETA . . . 102
6.8 IGOR2 E A SÍNTESE DE PROGRAMAS FUNCIONAIS GUIADOS POR EXEMPLOS E/S . . . 103
6.9 SÍNTESE DE PROGRAMAS COMPORTAMENTAL . . . 104
6.10 SÍNTESE DE PROGRAMAS COM ALGORITMO GENÉTICO E AI PRO-GRAMMING . . . 106
6.11 SÍNTESE GUIADA E CVC4SY . . . 107
7.1 RESUMO DAS CONTRIBUIÇÕES . . . 112 7.2 TRABALHO FUTURO . . . 114
REFERÊNCIAS . . . 116 APÊNDICE A – PSMF E FUTURAS IMPLEMENTAÇÕES . . . . 121
1 INTRODUÇÃO
Neste trabalho, apresentamos uma extensão de síntese de programas por localizador de modelo. Diante de sua natureza de concepção e cenário inicial, foi batizado de “Síntese de Programas via Localizador de Modelo” ou “Program Synthesis via Model Finder”, doravante PSMF. A extensão do PSMF, resultante deste trabalho, traz contribuições para mitigar 3 desafios na área de síntese de programas: (i) o problema da explosão de estados; (ii) a dificuldade de convergência para uma solução; e (iii) o gigantesco tamanho do espaço de busca e 4 dificuldades no PSMF inicial que o usuário tenta manualmente resolver: (i) definição dos ingredientes sintáticos; (ii) viés de cobertura do contrato de pré/pós condição fornecido ao sintetizador; (iii) definição de expressões de sketch; e (iv) sintetizar programas mais complexos que os existentes ou que usam arrays em seu código-fonte. Como a nova ferramenta PSMF, construída pensando no usuário final, ele é capaz de representar a sua intenção usando exemplos de entrada/saída (exemplos E/S),soft sketch (ou seja, um conjunto de comandos que devem aparecer no programa sintetizado, mas são escritos em qualquer ordem) e a saída gerada pelo PSMF é um programa imperativo de propósito geral.
Este capítulo relata o ponto de partida para realização deste trabalho: fornecendo uma justificativa (Seção 1.1); aspectos de motivação (Seção 1.2); as principais contribuições esperadas (Seção 1.3); além de mostrar como está estruturado o restante do documento (Seção 1.4).
1.1 JUSTIFICATIVA
O desenvolvimento de software requer a habilidade (entre outras) de escrever código-fonte em alguma linguagem, em outras palavras, programar. No contexto atual, a tarefa de programar tem sido executada por pessoas e, por isso, o resultado dos programas depende da experiência dos seres humanos nesta atividade e, por consequência, os erros nos programas advém do menor grau desta experiência.
Um dos objetivos da síntese de programas é mecanizar a tarefa de programar, criando um código-fonte executável a partir da intenção (ou especificação) do usuário. Essa tarefa é executada por um sintetizador que captura a intenção do usuário e realiza uma busca por programas candidatos à solução que atenda tal intenção, permitindo transferir às máquinas a tarefa de criar programas para problemas apresentados, possibilitando que uma maior parte do esforço humano seja dedicado à especificação dos problemas que os programas sintetizados vão tentar resolver.
Além disso, síntese de programas potencializa a redução de erros resultantes da progra-mação feita por humanos, como também as possibilidades de surgirem novas
implemen-tações de programas. A área encontra aplicação em alguns cenários, como por exemplo: (i) dotar pessoas sem conhecimento prévio em programação a desenvolver programas utilitários que requerem a mecanização de tarefas repetitivas; (ii) ajudar programadores regulares a descobrir detalhes ou truques de situações do cotidiano; (iii) entender progra-mas que antes se apresentavam obscuros; (iv) descobrir novos prograprogra-mas; e (v) ensinar programação (GULWANI, 2010).
Embora seja possível observar os avanços na área de síntese de programas em termos de aumentar o conforto do usuário, desde a década de 1970 até o presente, uma completa mecanização da tarefa de programar, a partir da intenção (ou representação da intenção) do usuário, permanece um desafio, seja porque: (i) escrever a especificação como requisito de entrada para o sintetizador chega a ser tão ou mais desafiador quanto programar; ou (ii) a complexidade dos problemas gera um grande espaço de busca, dificultando a tarefa do sintetizador de encontrar uma solução que atenda e seja dentro de um tempo razoável. Devido aos desafios listados no parágrafo anterior, a comunidade científica tem abor-dado o problema de síntese de diversas maneiras. Polozov e Gulwani (2015), Hofmann (2010), Becker e Gottschlich (2017), Reynolds et al. (2019) exploram síntese de progra-mas cujas entradas são exemplos e as saídas são prograprogra-mas de manipulação de strings. Manna e Waldinger (1971), Srivastava (2010), Polikarpova e Sergey (2019) recebem como entrada uma especificação formal e produzem programas de propósito geral. Solar-Lezama et al. (2006), Qiu e Solar-Lezama (2017), Torlak e Bodik (2013) recebem como entrada uma especificação formal e programas imperativos com lacunas (chamados de sketches) e retornam os programas completos.
Neste contexto, produzimos novas características a partir do sintetizador PSMF, que foi desenvolvido por Mota, Iyoda e Maranhão (2016). O PSMF é um sintetizador de pro-gramas imperativos de propósito geral, no domínio da aritmética de inteiros, que incor-pora a sintaxe e semântica da linguagem IMP(erativa) desenvolvida por Winskel (1993), foi implementado em Alloy*(MILICEVIC et al., 2015), que é uma extensão da linguagem Alloy (JACKSON, 2002) e usa um analisador de modelos Alloy* para encontrar programas candidatos que atendam à intenção do usuário. O PSMF teve êxito em sintetizar quatro programas bem conhecidos na literatura: troca de valor entre duas variáveis (Swap2); maior entre 2 números (Max2); maior entre 3 números (Max3) e máximo divisor comum ou Greatest Common Divisor (GCD), dentre outros programas usados como teste.
Contudo, o usuário precisa de uma considerável experiência (e de tempo) para intera-gir com o sintetizador PSMF (Seções 2.1, 2.4 e 2.5), definindo a sua intenção, através de: (i) contrato de pré/pós-condição; (ii) ingredientes sintáticos; e (iii) sketch; inspecionando cada programa candidato gerado pelo sintetizador para decidir se atende a intenção ou se continua procurando manualmente por outros, reforçando a necessidade de mais investi-gação sobre o tema.
1.2 MOTIVAÇÃO
Uma motivação inicial para realizar este trabalho foi estender as funcionalidades do PSMF (MOTA; IYODA; MARANHÃO, 2016) no sentido de facilitar seu uso pelo usuário final (por exemplo, diminuindo a intervenção manual) e produzir programas mais elabo-rados (por exemplo que usam vetores, doravanteArrays, em seu código-fonte). No contexto desta tese, o usuário final foi definido como aquele que tem pouca (ou mesmo nenhuma) experiência em programação de computadores.
Para dar robustez e direcionamento, decidiu-se explorar quatro desafios, que ocorrem também em outros sintetizadores disponíveis na literatura (Capítulo 6):
1. Uma forma menos desafiadora de especificar a intenção do usuário final, do que usar contrato de pré/pós-condição que é encontrada no atual PSMF;
2. Usar exemplos de entrada/saída buscando um equilíbrio de forma que não sejam muitos (que provoquem o problema de explosão de estados) nem sejam poucos (que prejudiquem a expressividade de representar a intenção do usuário);
3. Automatizar a tarefa de decidir, no extenso espaço de busca, qual programa candi-dato atende suficientemente a intenção do usuário; e
4. Do que já foi feito até aqui, sintetizar os mesmos e outros programas mais complexos, inclusive com Arrays no código-fonte.
Gulwani (2010) opina que na perspectiva do usuário final, a tarefa de especificar sua in-tenção com contrato de pré/pós-condição é mais desafiadora do que produzir exemplos de entrada/saída (doravante exemplos E/S). Isso foi motivação para estender o sintetizador PSMF inicial (que representa a intenção do usuário como pré/pós-condição, Seção 3.3), passando a usar como entrada exemplos E/S (ao invés de pré/pós-condição, Seção 3.4), incorporando a capacidade de sintetizar Arrays (Seção 3.5), e combinando algoritmo ge-nético, localizador de modelos e testes de programas candidatos, para mitigar o trabalho manual do usuário em: (i) definir os ingredientes sintáticos; (ii) executar a busca de pro-gramas candidatos; (iii) realizar testes; e (iv) decidir se o programa candidato atende a sua intenção (Seção 3.6).
Contudo, exemplos E/S precisam ser usados com cautela, pois a quantidade de exem-plos passados para o sintetizador Alloy* afeta diretamente o problema de explosão de
estados (onde o número de estados a ser representados pelo sintetizador supera o seu
li-mite de formulação do problema e, portanto, não é possível iniciar a busca por programas candidatos), inviabilizando o funcionamento do sintetizador (CLARKE et al., 2011; JACK-SON, 2002). Suponha o cenário onde um laço While usa 4 variáveis. Se o usuário definir 2 exemplos que faça o laço iterar 3 vezes em cada um dos exemplos, então o sintetizador pode gerar até 48 estados no analisador Alloy*: 4 (variáveis) × 3 (iterações do laço) ×
2 (estados: inicial e final) × 2 (exemplos). Agora se considerar apenas 1 exemplo que itere 2 vezes no laço, então o sintetizador pode gerar até 16 estados no analisador Alloy*. Visando mitigar este problema de explosão de estados, diversos sintetizadores exploram es-tratégias, como Síntese Indutiva Guiada por Contraexemplo ouCounter Example Guided
Inductive Synthesis (CEGIS) (ALUR et al., 2013), e tendem a usar o mínimo de exemplos (quantidade e simplicidade) quanto possível seja representar a intenção do usuário.
1.3 CONTRIBUIÇÕES
As principais contribuições esperadas deste trabalho são:
1. Novo PSMF → implementar (na teoria e prática) uma extensão do sintetizador PSMF inicial (que representa a intenção do usuário como pré/pós-condição), pas-sando a usar como entrada exemplos E/S, incorporando a capacidade de sintetizar
Arrays, e combinando algoritmo genético, localizador de modelos e testes de
pro-gramas candidatos, para mitigar 3 desafios na área de síntese de propro-gramas: (i) o problema da explosão de estados; (ii) a dificuldade de convergência para uma solu-ção; e (iii) o gigantesco tamanho do espaço de busca e 4 dificuldades no PSMF inicial que o usuário tenta manualmente resolver: (i) definição dos ingredientes sintáticos; (ii) viés de cobertura do contrato de pré/pós condição fornecido ao sintetizador; (iii) definição de expressões de sketch; e (iv) sintetizar programas mais complexos que os existentes ou que usam arrays em seu código-fonte. (Capítulo 3);
2. Um conjunto de programas sintetizados → entregar treze (seis deles com
Arrays) programas (Maj5, Maj8, Max4, Modu, Fact, IntSqrt, Fib, aMax, aDouble,
aSum, eCount, aBubSort e aSelSort) diferentes, além de quatro (Swap2, Max2, Max3 e GCD) que foram sintetizados até o início deste trabalho (Capítulo 4); 3. Outras fitness → implementar (na teoria e prática) cinco distâncias (Manhattan,
Euclidiana quadrática, Chebyshev, Hellinger, Minkowski) diferentes da distância discreta, como métrica para cálculo da função de fitness no sintetizador PSMF e verificar através de testes estatísticos em dois programas conhecidos (GCD e maior entre 4 números (Max4)) a partir de um conjunto de experimentos, se há diferença significativa entre as distâncias, determinando se existe alguma que melhora a con-vergência para uma solução (Capítulo 5);
4. Um posicionamento do PSMF na literatura → mapear diferentes abordagens para síntese de programas, colocando-as em um plano cartesiano onde a abscissa captura diversos tipos de entradas que os usuários fornecem ao sintetizador e a ordenada captura os tipos de programas sintetizados. Nesta perspectiva, posicionar o sintetizador PSMF (Capítulo 6); e
5. Futuras funcionalidades → apresentar uma implementação teórica de duas fu-turas funcionalidades para o PSMF, onde há uma expectativa de mitigar as dificul-dades D1 (Definição manual de sketches como restrições) e D2 (Não convergência para uma solução) a partir da implementação de três estratégias S1 (gerar mecani-camente expressões de sketches), S2 (adaptar a técnica de síntese comportamental) e S3 (que é usar S1 e S2 simultaneamente). As estratégias S1 e S2 são ilustradas a partir de um exemplo teórico da síntese do programa Max3; Um desenho experimen-tal inteiramente casualizado usando como fatores dois programas (GCD e Max3) e as três estratégias (S1, S2 e S3), além do fator de controle (S0) que é a ausência de S1, S2 e S3 também é apresentado (Anexo A).
6. PSMF como software público → que esteja disponível1 para que interessados possam repetir os resultados aqui gerados e sintetizar outros programas com similar nível de complexidade.
Assim, gera-se a expectativa de que se entregue um sintetizador “mais amigável” (ainda que seja necessária uma considerável ajuda do usuário final) visto que: a entrada (fornecida pelo usuário) passa a ser por exemplos,soft sketches e ingredientes sintáticos; a interação com o usuário é apenas na etapa inicial da tarefa, ou seja, depois de preparar a entrada, todo o restante fica por conta da mecanização do sintetizador; e a saída consiga produzir programas imperativos de propósito geral, inclusive que usam arrays.
1.4 ESTRUTURA DESTE DOCUMENTO
O restante deste documento está estruturado da seguinte maneira:
Capítulo 2 apresenta os principais conceitos que fundamentaram o presente traba-lho, disponíveis na literatura atual, como: Síntese de programas (2.1), Aprendizagem de máquina (2.2), Algoritmo e Programação Genética (2.3), Abstração de software com lin-guagem Alloy (2.4) e o estado do sintetizador PSMF antes da realização deste trabalho (2.5).
Capítulo 3 apresenta o PSMF com o objetivo aproximar (no sentido de ser menos desafiador de usar) a área de síntese de programas do usuário final, visto as dificulda-des apontadas na tarefa de síntese (Seção 2.1.2) e no sintetizador Alloy* existente (Se-ção 2.5.3). Contribuições são apresentadas (Se(Se-ção 3.2) como uma proposta e uma imple-mentação de um sistema que a partir de um cenário inicial (Seção 3.3), muda o paradigma de representação da intenção do usuário (de dedutivo para indutivo, Seção 3.4), aumenta as possibilidades de síntese incluindo Arrays (Seção 3.5), e que combina algoritmo gené-tico, localizador de modelo e testes de programas candidatos, para mitigar o problema da explosão de estados e o trabalho do usuário em: (i) definir os ingredientes sintáticos; (ii) 1 Mais informação no website do projeto PSMF: https://github.com/PSMFg/psmf/wiki.
executar manualmente a busca de programas candidatos; (iii) realizar testes; e (iv) deci-dir se o programa candidato atende a sua intenção (Seção 3.6). Conclusões são emitidas (Seção 3.7).
Capítulo 4 apresenta um conjunto de experimentos e avaliação do problema de sín-tese de programas enunciado nos capítulos 1 e 2 e de uma solução proposta no Capítulo 3. Além das contribuições (Seção 4.1), o comportamento do PSMF é analisado a partir da definição de um conjunto de parâmetros do sistema, que caracterizam um cromossomo
feliz (como sendo um cromossomo que guia a busca para uma solução, com 100% de
probabilidade de acerto), com experimentos conduzidos na síntese de dezesseis programas clássicos, sem (Seção 4.2) e comArrays (Seção 4.3). Em seguida, os parâmetros que carac-terizam o cromossomo feliz são relaxados, aumentando o espaço de busca 16 vezes (no Fib) e 64 vezes (no Maj5), para verificar a efetividade da estratégia de algoritmo genético no PSMF (Seção 4.4). Os resultados dos experimentos mostram que algumas das dificuldades apontadas na tarefa de síntese (Seção 2.1.2) e no PSMF inicial (Seção 2.5.3) são mitiga-das. Ainda que restem algumas situações de exceção, propostas de como enfrentá-las e conclusões são emitidas, potencializando o uso do PSMF pelo usuário final (Seção 4.5).
Capítulo 5 apresenta uma proposta para melhorar a convergência para uma solução na tarefa de síntese (Seção 2.1.2). Para isso, cinco distâncias diferentes da métrica dis-creta (através de uma estratégia aqui denominada de S1) são implementadas no PSMF, permitindo ao usuário escolher uma das seis disponíveis (discreta, Manhattan, Euclidi-ana quadrática, Chebyshev, Hellinger, Minkowski) de um conjunto de distâncias como métrica do cálculo da fitness (Seção 5.3). Contribuições são elencadas (Seção 5.2), o com-portamento do PSMF é analisado a partir da definição de um conjunto de parâmetros do sistema com experimentos conduzidos na síntese de dois programas conhecidos (GCD e Max4) e resultados são apresentados (Seção 5.4). Dificuldades, com propostas de como contorná-las, são apresentadas (Seção 5.5) e conclusões são emitidas (Seção 5.6).
Capítulo 6 Este capítulo apresenta outras onze estratégias de síntese de programas encontradas na literatura, através de uma abordagem de síntese: a partir de especificação com prova por indução matemática (Seção 6.1); de programas com buracos sintáticos, preenchidos por um sintetizador combinacional (Seção 6.2); por prova teórica, a partir da construção de uma estrutura de scaffold (Seção 6.3); “auxiliada por resolvedores” através de sketches (Seção 6.4); que usa prova natural para gerar programas extraídos de especifi-cações de alto nível (Seção 6.5); decompondo especifiespecifi-cações mais complexas em mais sim-ples, em um sintetizador de estruturas de dados (recursivas) que manipulam (programas imperativos) pilhas alocadas dinamicamente (Seção 6.6); de programas que manipulam strings gerados por exemplos (Seção 6.7); de programas funcionais e recursivos guiados por exemplos E/S (Seção 6.8); explorando os estados inicial, final e intermediário dos pro-gramas candidatos, com programação genética e aprendizagem de máquina (Seção 6.9); por algorítimo genético para gerar programas aleatórios com uma curta linguagem de
baixo nível (Seção 6.10); com três estratégias de enumeração, tornando a busca esperta, rápida ou híbrida (Seção 6.11); e posiciona o PSMF a partir de algumas diferenças e do que foi sintetizado em relação às abordagens apresentadas (Seção 6.12).
Conclusão apresenta as considerações finais, enumera as contribuições alcançadas e resume as indicações de trabalhos futuros.
Anexo A apresenta novas funcionalidades a serem incorporadas no PSMF (Seção A.1) gerando uma expectativa de agregar mais contribuições (Seção A.2). Duas estratégias são teorizadas, ainda que não incorporadas no sintetizador PSMF: S1 → a geração mecânica de expressões de sketches a partir da extensão da implementação da técnica de algoritmo genético (Seção A.3); e S2 → uma adaptação da técnica de síntese comportamental (Se-ção A.4). Um conjunto de experimentos é projetado (Se(Se-ção A.5) para serem usados em alguns dos programas sintetizados anteriormente. Finalmente, são apresentadas algumas dificuldades (Seção A.6) e conclusões são emitidas (Seção A.7).
2 FUNDAMENTAÇÃO TEÓRICA
Este capítulo descreve os fundamentos que guiarão o presente trabalho, como: síntese de programas e as características de um sintetizador (Seção 2.1), os principais paradigmas da área (Seção 2.1.1) e desafios (Seção 2.1.2); Aprendizagem de Máquina com alguns de seus paradigmas e aplicações (Seção 2.2) e alguns paradigmas de aprendizagem indutiva voltados para a tarefa de classificação (Seção 2.2.1); Algoritmos e Programação Genética (Seção 2.3), alguns operadores de seleção e de busca (Seção 2.3.1) e alguns desafios que a técnica impõe (Seção 2.3.2); Abstração de software, linguagem Alloy e uma de suas extensões, a Alloy* (Seção 2.4) usada na construção do sintetizador adotado (Seção 2.5) para a realização deste trabalho (Seção 2.5.1), um exemplo numérico para ilustrar o seu funcionamento (Seção 2.5.2) e as principais dificuldades em utilizá-lo (Seção 2.5.3); e conclusões são emitidas à cerca das escolhas para fundamentar o trabalho (Seção 2.6).
2.1 SÍNTESE DE PROGRAMAS
Síntese de programas é uma tarefa que tem o objetivo de descobrir, mecanicamente (ou seja, através de uma ferramenta denominada de sintetizador) um programa que atenda à intenção do usuário. Tal intenção é expressa como um conjunto de restrições impostas a um sintetizador. A intenção do usuário pode ser bem variada, mas as mais utilizadas são: estrutura do próprio código-fonte esperado com lacunas a serem automaticamente preenchidas pelo sintetizador (sketch), especificação formal e exemplos E/S.
Formalizando, todas as possíveis maneiras de representar a intenção do usuário in-duzem a uma intenção ideal e completa 𝐸 como uma relação entre pares de estados iniciais e finais do sistema. Seja 𝑋 o conjunto de todos os possíveis estados iniciais e 𝑌 o conjunto de todos os possíveis respectivos estados finais, então 𝐸 ⊆ 𝑋×𝑌 . Seja 𝑃 o conjunto de todos os possíveis programas que transformam 𝑋 em seus respectivos 𝑌 , ou seja 𝑃 : 𝑋 → 𝑌 . Um programa 𝑝 (𝑝 ∈ 𝑃 ) pode ser interpretado como uma função que transforma um estado inicial em um estado final 𝑝 : 𝑥 → 𝑦 e a síntese ocorrerá com sucesso quando for possível gerar um programa 𝑝* (𝑝* ∈ 𝑃 ) que torne válida a expressão ∀(𝑥, 𝑦) ∈ 𝐸 =⇒ (∃𝑝* : 𝑋 → 𝑌 |(𝑝*(𝑥) = 𝑦)). Ou seja, o sintetizador recebe a intenção do usuário e gera um programa que transforma estados iniciais em respectivos estados finais (estados válidos no domínio da intenção).
Um sintetizador de programas é usualmente caracterizado por três dimensões (GULWANI, 2010):
1. Intenção do usuário → É representada por um conjunto de restrições que o sintetizador aceita, para expressar a intenção. A intenção do usuário final pode ter
diferentes tipos, o primeiro seria o mais desafiador e o último o menos desafiador de se produzir, por exemplo:
a) Especificação formal. É uma relação lógica entre entrada e saída, frequente-mente representada na forma de um contrato de pré e pós condição;
b) Programas ineficientes ou incompletos. Pode ser tanto um programa ineficiente em que o sintetizador tentará otimizar quanto, por exemplo, o código-fonte (ou parte dele) com algumas lacunas sintáticas (ou sketch) do programa a ser sintetizado;
c) Linguagem natural. É uma descrição em linguagem natural do programa. d) Exemplos E/S. Casos concretos e específicos de entradas e saídas esperadas
que o programa vai manipular.
2. Espaço de busca → É o espaço de possibilidades de programas onde o sintetizador vai fazer a busca, que pode ser uma linguagem de propósito geral ou uma Linguagem de Domínio Específco ou Domain-Specifc Language (DSL).
3. Mecanismo de busca utilizado → A busca pelo programa pode ser feita de diversas maneiras:
a) Força bruta. técnica onde as possíveis combinações de programas candidatos vão sendo testadas uma a uma, pode haver uma ordenação baseada na quan-tidade de ingredientes sintáticos (testar primeiro os programas com menos ingredientes sintáticos) e não há critério de avaliação para guiar a busca. Tem sido usada como ponto de partida no uso de mecanismos de busca, ou quando se percebe que se gastará mais tempo em usar outra técnica;
b) Técnicas de aprendizagem de máquina. Há várias técnicas, como, por exemplo, inferência probabilística, que modela um programa como um grafo com instru-ções e estados conectados por nós de restrição, onde tais restriinstru-ções representam a semântica ao estabelecer uma relação dos estados imediatamente anterior/-posterior da execução das instruções; ou programação genética, detalhado na Seção 2.2, dentre outras; e
c) Resolução lógica. Quando a tarefa de síntese é transformada em um problema de satisfabilidade ou Satisfability (SAT) ou satisfatibilidade módulo teoria ou
Satisfability Modulo Theory (SMT), e então são utilizados os solucionadores
(ou solvers) SAT/SMT para explorar esse espaço de busca por uma solução que satisfaça o conjunto de restrições.
2.1.1 Paradigmas de síntese de programas
Síntese de programas de sistemas de propósito geral produz programas que recebem a en-trada uma única vez e retornam um programa como saída. Basin et al. (2004) e Srivastava (2010) sugerem a seguinte classificação de síntese de programas:
1. Síntese dedutiva → Ao gerar programas que atendem a uma especificação formal, eles são considerados “corretos por construção”, pois vão gerar saídas matemati-camente corretas para todos os casos possíveis de entrada (neste caso, assumimos que a especificação é completa). A síntese dedutiva usa princípios de prova mate-mática.Ocorrem pelo menos duas dificuldades nesta abordagem: (i) quão desafiador será para o usuário produzir uma especificação formal completa para abarcar todos os comportamentos de um programa desejado e (ii) na eficiência do provador de teoremas (MANNA; WALDINGER, 1986).
2. Síntese guiada por esquema → Devido à dificuldade de mecanização de síntese dedutiva, a proposta aqui seria possibilitar algum nível de interação com o usuário, que provê um modelo computacional ou template para o programa desejado a ser sintetizado, e este será usado em conjunto com alguma abordagem dedutiva para realizar a tarefa de síntese. Esta abordagem tem sido usada com êxito em alguns casos de síntese de programas de propósito geral (COLÓN, 2004; SOLAR-LEZAMA et al., 2006; TORLAK; BODIK, 2013).
3. Síntese indutiva → Ao contrário da dedutiva, aqui a intenção é incompleta, sendo usualmente exemplos, na forma de uma lista de valores concretos de entradas e saídas (no qual não se obriga a enumerar todos os casos). A tarefa de síntese aqui é na forma de aprendizagem indutiva em busca da generalização. Ou seja, a partir de um pequeno conjunto de valores conhecidos de entrada/saída, tenta-se gerar programas que executem estes exemplos usados e também passem nos testes (que são diferentes dos exemplos) (SHAPIRO, 1981).
4. Síntese guiada por solucionadores SAT/SMT → O usuário precisa informar uma especificação funcional da entrada/saída, uma descrição das operações atômi-cas e uma restrição de recursos. A ideia principal da abordagem é tratar a tarefa de síntese como uma generalização da tarefa de verificação de programas, na qual o sintetizador cria um programa com uma estrutura de incógnitas (declarações, guar-das, invariantes e funções de ranking) e, em seguida, codifica estas restrições que se relacionam com as incógnitas no formato esperado das ferramentas SAT/SMT existentes de verificação (SRIVASTAVA, 2010).
2.1.2 Desafios em síntese de programas
Alguns desafios impedem a possibilidade de alargar o desempenho e a variedade de pro-blemas dos atuais sintetizadores de programas. Por exemplo:
1. O problema da explosão de estados → Os estados do sintetizador crescem exponencialmente com as possíveis combinações das instruções (ou seja, se apenas for considerada a sintaxe). Para ter-se uma ideia da velocidade do crescimento dos estados, imagine um programa que contém 4 variáveis dentro de um único laço
while. A cada volta no laço o sintetizador precisa armazenar até 8 estados (2 valores
para cada uma das 4 variáveis). Note que o problema da explosão de estados ocorre antes mesmo de se iniciar o processo de busca por programas candidatos, porque a quantidade de incógnitas na formulação das restrições é de tal magnitude, que excede a capacidade limite do analisador, e não se consegue passar para a etapa de busca (CLARKE et al., 2011);
2. Dificuldade de convergência para uma solução → Devido à riqueza das lin-guagens de programação, é possível expressar a mesma funcionalidade de formas sintáticas distintas. Ou seja, é possível que exista um conjunto de programas candi-datos corretos (alvos) para uma determinada tarefa de síntese, essa característica é chamada deMultimodalidade. Em princípio, a multimodalidade pode ser vista como algo interessante por prover a existência de mais de uma solução (alvo) para um problema, ou seja, mais possibilidades de sucesso dentro de um espaço de busca. Contudo, a depender de como os programas que solucionam o problema e como os alvos estão posicionados, a multimodalidade acrescenta um fator de incerteza de busca (para qual dos alvos seguir?). E essa incerteza pode tornar mais complexa a tarefa de busca do sintetizador, visto que o processo de decisão é construído ao longo da busca, baseado nas características disponíveis até o momento e sem saber quão próximo se está de um alvo. Chamamos essa dificuldade de decidir para qual alvo seguir de problema de convergência para uma solução. Portanto, não se pode afirmar que um programa candidato que passa em 80% dos testes é melhor (no sentido de que está mais próximo de convergir) que outro que passa em 60%, pois um pequena mudança sintática no programa dos 60% pode torná-lo um programa correto. Ou seja, nessa perspectiva, a multimodalidade (existência de vários alvos) pode dificultar o trabalho de busca do sintetizador para alcançar um alvo (ou seja, dificuldade de convergência); e
3. O Tamanho do espaço de busca → Combinando os dois problemas anteriores, surge a questão do espaço de busca por programas candidatos que cresce expo-nencialmente. Para ter-se uma ideia do tamanho do espaço de busca, imaginemos as assinaturas Add, Assign e IntVar e uma faixa de valores variando de 0 até 3
(escopo de 4 valores). Grosso modo, o espaço de busca seria o arranjo das cardi-nalidades das assinaturas de forma que o sintetizador poderá buscar por (0..3 Adi-ções) × (0..3 AtribuiAdi-ções) × (0..3 variáveis inteiras), ou seja, até 64 possibilidades (4 × 4 × 4 = 43 = 64). Se aumentarmos o escopo para a faixa de 0 até 5 nas mesmas 3 assinaturas, teremos 63 = 216 possibilidades. Assim, o conhecimento do usuário pode ter uma relação direta com a velocidade no qual o sintetizador encontra uma solução porque é possível fornecer informação na forma de restrições (porsketch ou por ingrediente sintático) de modo a diminuir consideravelmente o espaço de busca do sintetizador. As combinações sintáticas presentes na síntese dos programas deste trabalho chegam rapidamente a bilhões de possibilidades (visto que possível mani-pular até 18 ingredientes sintáticos. Ao pensarmos em 14 ingredientes em um escopo de 6 valores, alcançaremos pouco mais de 78 bilhões de possibilidades de programas candidatos (614), podendo gerar tempos de busca que inviabilize a tarefa de síntese. O sintetizador PSMF inicial (seções 2.5 e 3.3) usa mecanismo de busca por satisfati-bilidade e a intenção do usuário é representada por: contrato de pré/pós-condição, sketch e ingredientes sintáticos. No PSMF proposto (3.6) ocorre a mudança de um dos compo-nentes (de pré/pós-condição para exemplos E/S) que representam a intenção do usuário, ou seja, há uma mudança de paradigma (de dedutivo para indutivo) da representação da intenção do usuário (3.4).
Contudo, a tarefa de síntese no PSMF é sempre indutiva, sendo usada a estratégia CEGIS. CEGIS tende a ser bem eficiente em síntese de programas, porque tenta fazer generalizações a partir de um pequeno conjunto de estados (iniciais e finais) e endereça fórmulas de alta ordem, ou seja ∃𝑝∀𝑥 · 𝑠(𝑝, 𝑥), onde 𝑠 é uma especificação, 𝑥 uma en-trada e 𝑝 um programa. CEGIS executa três passos: (i) busca → tenta encontrar um programa candidato para resolver o problema de primeira ordem ∃𝑝∃𝑥 · 𝑠(𝑝, 𝑥); (ii) ve-rificação → se um programa candidato $𝑝 é encontrado, tenta verificar se ele é válido para todos as entradas, ou seja, a condição ∀𝑥 · 𝑠($𝑝, 𝑥). Essa verificação é feita tentando encontrar se a negativa da condição é insatisfeita, ou seja ∀𝑥 · 𝑠($𝑝, 𝑥) → 𝑆𝑎𝑡 se torna ∃𝑥 · ¬𝑠($𝑝, 𝑥) → 𝑈 𝑛𝑆𝑎𝑡 que também é de primeira ordem. (iii) indução → se a negativa da condição é insatisfeita, então a condição é satisfeita, o programa candidato é verificado e o sintetizador irá apresentá-o como solução. Do contrário, um contraexemplo $𝑥𝑐𝑒𝑥 é
gerado. A busca continua para o próximo programa candidato que precisa satisfazer a condição inicial mais o contraexemplo, ou seja ∃𝑝∃𝑥 · 𝑠(𝑝, 𝑥) ∧ 𝑠(𝑝, $𝑥𝑐𝑒𝑥). E assim
suces-sivamente até que se encontre um programa candidato que satisfaça a condição inicial e o conjunto de contraexemplos gerados ou o problema da referida síntese seja considerado insolúvel (MILICEVIC et al., 2015).
2.2 APRENDIZAGEM DE MÁQUINA
Aprendizagem de Máquina ou Machine Learning (ML) tem o objetivo de dotar progra-mas de computador com uma capacidade de melhorar seu próprio desempenho, em uma determinada tarefa, a partir da experiência adquirida. ML tem sido construída a partir de contribuições de diferentes campos do conhecimento, como: Probabilidade e Estatística, Inteligência Artificial, Teoria da Informação, Psicologia, Neurobiologia, Complexidade Computacional, etc.
ML é uma das principais vertentes de investigação na área da Inteligência Artificial ou
Artificial Intelligence (AI). Duas razões principais para tal destaque: (i) a capacidade de
aprendizagem é um forte indicativo de comportamento inteligente; e (ii) a aprendizagem aumenta o potencial de construir sistemas com melhor desempenho ou para atuar em problemas mais complexos.
ML pode ser aplicado a um amplo conjunto de problemas. Alpaydin (2009) propõe pelo menos quatro categorias de problemas que bem resumem muitas aplicações encontradas na atualidade:
1. Classificação binária → Seja 𝑑 um conjunto de dados (valores de entradas) exis-tentes dentro um domínio 𝒟 (𝑑 ∈ 𝒟). Deseja-se conhecer o comportamento de uma variável aleatória 𝑦 com valores de saída 0 ou 1 (ou seja, 𝑦 = 𝑓 (𝒟) → {0, 1}) em função dos dados existentes 𝑑 ou para novas entradas 𝑑′ (note que 𝑑′ pertence ao complemento de 𝑑, ou seja 𝑑′ ⊆ 𝑑𝑐), que permita classificar corretamente 𝑦 em
qualquer parte do domínio 𝒟;
2. Classificação múltipla → Seria uma extensão da classificação binária tendo em vista que 𝑦 agora assume uma faixa de diferentes valores inteiros 𝑦 ∈ {1, 2, . . . , 𝑛}; 3. Regressão → Dado um conjunto de dados dentro um domínio 𝒟, tenta-se prever
o comportamento de uma variável aleatória 𝑦 ∈ R como uma função 𝑦 = 𝑓 (𝒟) que mapeia os dados em 𝒟 para uma curva que esteja mais próxima de cada 𝑑, e assim o faça para uma nova entrada 𝑑′ no domínio 𝒟;
4. Detecção de novidades → Dado um conjunto de observações em um domínio 𝒟, constrói-se um padrão e, a partir de então, vai-se em busca de identificar se novas observações possuem um comportamento “muito diferente” do padrão construído. Neste contexto, “novidade” remete para algo que raramente ocorre; e “muito dife-rente” remete para que, a cada observação realizada, haja um valor de similaridade calculado e, se este valor estiver fora da faixa definida no padrão, então essa nova observação é considerada uma novidade.
1. Supervisionada → É informado ao sistema qual o valor de referência (alvo). Por exemplo, numa tarefa de reconhecimento de imagens, a cada dado 𝑑 (imagem) da base de treino 𝒟 (contendo imagens de pessoas, animais ou lugares) é informado ao sistema o que é cada entidade (ou seja, é colocado um rótulo em cada imagem). Outro exemplo, no domínio dos números inteiros, ao se desejar sintetizar um pro-grama que atenda os dados de entrada e gere a saída esperada, o sistema precisa ter conhecimento dos valores de entrada e de saída.
2. Não supervisionada → Não é informado ao sistema qual o valor de referência (alvo) e, portanto, ele tenta gerar agrupamentos (clustering), com base nas seme-lhanças entre dos dados em 𝒟. Tomando o exemplo anterior, o sistema poderá agru-par três conjuntos de imagens, mas não saberá, entre os grupos, qual o de pessoas, animais ou lugares.
3. Semi-supervisionada → Diante de um grande conjunto de dados, apenas uma pequena parte tem valor de referência (alvo). Então, o sistema inicia a aprendizagem de uma forma supervisionada e encontra mecanicamente um caminho para aprender sobre como procederá com os demais dados. Ainda no exemplo, é como se, após a definição de rótulos em algumas imagens de pessoas, animais ou lugares, o sistema identificasse um padrão e classificasse o restante dos dados em 𝒟, sem a necessidade de acompanhamento. Outro exemplo, no domínio dos números inteiros, onde se desejar sintetizar um programa que atenda os dados de entrada e gere a saída esperada, o sistema precisa ter conhecimento de uns poucos valores dos dados de entrada e respectiva saída. E, em seguida, conseguisse generalizar para qualquer outro conjunto de dados no mesmo domínio 𝒟.
ML encontra aplicação em pelo menos quatro situações do cotidiano:
1. Rankings → Criar rankings (ordenado por semelhança e relevância) de páginas web baseada em consulta submetida num site de busca na Internet.
2. Filtragem colaborativa → Com base no comportamento de navegação (conteúdos de interesse) do usuário, o site tenta prever os próximos e oferecer conteúdo relevante ou que seja do interesse do usuário.
3. Tradução automática de conteúdo → Um sistema poderia aprender a traduzir entre duas línguas a partir dos documentos de tradução disponíveis.
4. Classificação → Para reconhecimento de faces de pessoas, objetos, locais, ações e voz.
Entre os desafios em ML, pode-se citar o crescimento dos repositórios de conteúdos e as demandas por informações relacionadas que aumentam em tamanho e complexidade.
A geração de conhecimento a partir desses conteúdos tem um custo (de tempo ou recursos computacionais) que pode ser medido (grosso modo) como o esforço para localizar, extrair, organizar e disponibilizar o conhecimento desejado (NILSSON, 1996).
2.2.1 Classificação por aprendizagem indutiva
Uma das formas de se executar a tarefa de síntese de programas pode ser entendida como uma tentativa de generalizar, a partir da observação de um pequeno conjunto de exemplos, a execução de um programa. Ou seja, implementar uma aprendizagem indutiva. Mitchell (1999) apresenta pelo menos quatro métodos de aprendizagem indutiva:
1. Árvores de decisão ou decision trees → Define um modelo para predizer valores-alvo (usualmente discretos) a partir de uma função de aprendizagem que é repre-sentada por uma árvore de decisão. Um conjunto de regras do tipo if-then-else pode ser extraído da árvore de decisão.
2. Redes neurais → É um modelo para predizer valores-alvo (discretos, contínuos ou vetores) a partir de uma função de aprendizagem que é representada por uma rede de nodos (neurônios artificiais) interconectados com diferentes pesos e compor-tamentos, de forma a gerar valores de saída a partir da manipulação dos dados de entrada.
3. Bayesiana → É um modelo probabilístico para 𝑃 (ℎ|𝑑) = 𝑃 (𝑑|ℎ)× ℎ𝑃 (𝑑) que calcula a probabilidade de ocorrência de uma hipótese ou valor-alvo 𝑃 (ℎ|𝑑) a partir das probabilidades de dados onde a hipótese foi verdadeira 𝑃 (𝑑|ℎ), onde a hipótese é verdadeira independente dos dados 𝑝(ℎ) e dos dados independente da hipótese 𝑝(𝑑). Depois de calcular 𝑃 (ℎ|𝑑) de várias hipóteses, escolhe-se a hipótese com máxima probabilidade.
4. K vizinhos mais próximos ou K nearest neighbors (KNN) → Para predi-zer um novo valor-alvo, calcula-se a distância dos 𝑘 (número inteiro) pontos mais próximos, sendo esse novo valor da mesma classe da maioria dos seus 𝑘 vizinhos. No PSMF proposto, pode-se dizer que a aprendizagem é indutiva e oscila em todos os níveis de supervisão, pois, em princípio, o usuário não conhece o código-fonte do programa que ele quer sintetizar (“não supervisionada”), mas conhece alguns exemplos E/S (“super-visionada”) e pode dar algumas pistas da estrutura (“semi-super(“super-visionada”) fornecendo
sketch;
2.3 ALGORITMOS GENÉTICOS E PROGRAMAÇÃO GENÉTICA
Algoritmos genéticos ou Genetic Algorithms (GA) fazem parte da família de modelos computacionais inspirados em evolução natural (ou computação evolucionária). Esses
al-goritmos vão em busca de potenciais soluções de um problema específico usando uma estrutura de dados baseada em cromossomos, que são indivíduos que representam uma potencial solução. A implementação de um algoritmo genético se inicia com uma popu-lação de cromossomos que, geração após geração, tem oportunidades de recombinação (mutação, cruzamento, etc) de forma a obter novos cromossomos “esperançosamente me-lhores” (mais próximos da solução do problema). Ao final de cada geração, a semelhança de cada cromossomo é calculada com base na aproximação de um valor desejado, tido como valor de referência e determinado por uma função defitness. Passam para a geração seguinte aqueles indivíduos mais aptos (com maiores probabilidades de serem escolhidos com base no seu valor de fitness). Assim, ao final do processo evolucionário, é de se espe-rar que o melhor cromossomo (que se conseguiu geespe-rar durante o processo evolucionário) represente a solução do problema (o que não implica que foi necessariamente a melhor solução entre todas as possíveis). Algoritmos Genéticos têm sido vistos como otimizadores de funções, porém, têm sido adotados para resolver uma vasta quantidade de problemas, inclusive síntese de programas indutiva (KOZA, 1995).
2.3.1 Programação genética, operadores de seleção e busca
Programação genética (POLI et al., 2008) ou Genetic Programming (GP) guarda muitas similaridades com Algoritmos Genéticos, ou seja, também é um processo estocástico, tra-tado como otimizadores de funções e que se aplicam oportunamente à síntese de programas indutiva. Neste caso, as populações são definidas como programas candidatos (indivíduos) que têm oportunidades de recombinação (mutação, cruzamento, etc) de partes do seu código-fonte (subprogramas) de forma a obter novos indivíduos “esperançosamente” mais próximos da solução do problema (ou seja, de um código-fonte desejado).
Sendo um processo iterativo de seleção e busca, a programação genética ocorre através das seguintes etapas: (i) Inicialmente é gerada aleatoriamente uma população de progra-mas candidatos 𝑃 dentro do escopo de um problema; (ii) Em seguida, a qualidade de cada programa candidato 𝑝 ∈ 𝑃 é avaliada, através de uma função de fitness, ou seja, o programa é executado contra um conjunto de testes e a saída gerada é comparada com a saída esperada, tendo melhor valor de fitness aquele programa que teve menor quantidade de erros; (iii) Se a avaliação apresentar um programa 𝑝* ∈ 𝑃 que atende à especificação (sem erros), a busca é finalizada e o programa 𝑝* é devolvido como uma solução desejada; (iv) Do contrário, um operador de seleção é aplicado a 𝑃 , produzindo um subconjunto 𝑃′ ⊆ 𝑃 dos programas mais promissores, denominado pais ou parents; (v) Em seguida, operadores de busca (tipicamente, mutação e cruzamento) são aplicados aos elementos de 𝑃′, resultando em programas candidatos filhos ou offsprings 𝑃′′; e (vi) Que vão formar a próxima população a ser utilizada na iteração subsequente do processo evolucionário (BURKE; GUSTAFSON; KENDALL, 2002).
por uma solução adequada, é importante frisar que os operadores de seleção e de busca também desempenham papéis importantes (BLICKLE; THIELE, 1996).
Convém descrever pelo menos três operadores de seleção:
1. Seleção Proporcional ou fitness-proportionate selection → Cada programa candidato usa o seu valor defitness proporcional à probabilidade de serem seleciona-dos, mas a escolha ocorrerá de forma aleatória. Ou seja, embora os indivíduos com melhores valores de fitness tenham uma possibilidade maior de serem selecionados, pode ocorrer que indivíduos menos aptos (com valor de fitness piores) possam ser selecionados, garantindo assim alguma diversidade. A escolha aleatória se parece com o girar de uma roleta que pode parar sua agulha em qualquer “posição”. É um método muito popular em algoritmo genético, mas precisa que sejam implementa-dos dois artifícios: fazer uma escolha aleatória sobre a seleção (por exemplo, escolher 80% de todos os indivíduos dos 20% melhoresfitness) e adequar a escala aos limites de valores de fitness encontrados na população.
2. Seleção por torneio ou tournament selection → Escolhe-se aleatoriamente da população um grupo de 𝑡 (𝑡 = 2, 3, . . . , 𝑛) indivíduos e o de melhorfitness é copiado para um repositório intermediário, repetindo o processo até que se tenha a próxima geração. É o operador de seleção mais popular (e tido por default) em programação genética.
3. Seleção por truncamento ou truncation selection → Com base em um valor limiar 𝑡𝑠 entre zero e um, 𝑡𝑠 = {0, 1}, apenas os 𝑡𝑠 melhores podem ser selecionados e todos eles têm a mesma probabilidade. Por exemplo, se 𝑡𝑠 = 0.2, então a seleção é feita entre os 20% da população com os melhores valores de fitness, sendo os outros 80% dos indivíduos descartados.
E dois operadores de busca:
1. Mutação → Tem o objetivo de introduzir mudanças em um programa candidato. Por exemplo, a árvore sintática abstrata ou Abstract Syntax Tree (AST) de um pro-grama candidato vai ter um de seus nós aleatoriamente substituído por um pedaço de programa (uma sub-árvore ou subprograma) gerado no processo evolucionário. 2. Cruzamento → Tem o objetivo de recombinar programas candidatos (parents)
para que os seus filhos (offsprings) levem para a próxima geração algumas caracte-rísticas dos seus pais (parents).
2.3.2 Dificuldades em algoritmo e programação genética
São apresentadas cinco dificuldades que devem ser percebidas quando se usa algoritmo ou programação genética:
1. Dificuldade de convergência para uma solução → O cenário mais conhecido é quando dois (ou mais) programas candidatos obtém o mesmo valor de fitness. Como não se tem um “programa correto” para ser usado como referência porque a tarefa de síntese parte do princípio que o usuário não sabe como seria um “programa correto”, então o sintetizador terá dificuldade para guiar a busca. Assim, a escolha aleatória traz boas chances de não se conseguir a convergência para uma solução. 2. Não há garantia de se encontrar um ótimo global → A abordagem apresenta
como solução da tarefa de síntese o primeiro programa que atenda à especificação. Assim, as chances deste ser o melhor programa são pequenas haja visto o tamanho do espaço de busca e a multimodalidade (Seção 2.1.2) impostas pela natureza da síntese de programas.
3. Consumo de tempo para ter resultados interessantes → É comum as tarefas de síntese levarem horas ou mesmos dias para problemas que vão além dos exemplos ilustrativos (toy examples);
4. Parâmetros para execução da tarefa → Ajustar os parâmetros do algoritmo ou da programação genética para se chegar a uma solução pode se tornar um processo de tentativa e erro.
Em termos de algoritmo genético, a técnica de seleção proporcional, por ser a mais popular das existentes, será usada no PSMF proposto. Também será explorado o aspecto da aleatoriedade tanto na seleção quanto nas mutações dos ingredientes sintáticos. Em termos de programação genética, embora o analisador Alloy* possua o método next() que se comporta como uma mutação do código-fonte de um programa candidato (Seção 3.6.2), não utilizaremos Programação Genética no momento.
2.4 ABSTRAÇÃO, MODELOS E LINGUAGEM ALLOY
Jackson (2002) argumenta que abstração de software é uma questão central para o ade-quado desenvolvimento de software, pois uma abstração não é um módulo, interface, classe ou método, mas sim uma estrutura que representa uma ideia de como um problema (e suas futuras possíveis variações) pode ser solucionado. A prática de desenvolvimento de software deveria se deter primeiramente em definir uma abstração e depois confeccionar o código-fonte. Contudo, o que se vê frequentemente são tentativas (muitas vezes mal sucedidas) de explorar abstração, codificando. Sendo a razão do insucesso justamente porque código-fonte é uma ferramenta ineficiente para explorar abstração de software. Uma abordagem inovadora em métodos formais é aquela que provê uma notação sufici-entemente precisa e expressiva baseada em um simples e robusto conjunto de conceitos, substituindo a análise baseada em prova matemática (que é completa) por uma análise denominada métodos formais leves ou lightweight formal methods, que é completamente
automática, que visita apenas um pequeno espaço de casos e é, portanto, incompleta, mas que apresenta resultados imediatos.
Jackson (2002) desenvolveu uma linguagem de especificação declarativa denominada Alloy, visando construir abstração de software (modelos) baseada em lógica de primeira ordem. Assim, três conceitos fundamentais definem a abordagem:
1. Lógica → As estruturas (átomos, conjuntos, relações, assinaturas, campos) são re-presentadas como relações e suas respectivas propriedades são rere-presentadas por operadores. Estados e execuções são descritos usando restrições (expressões lógi-cas introduzidas pelo programador), que podem ser refinadas para representar os comportamentos desejados no modelo.
2. Linguagem → Complementa a lógica, permitindo generalizar estruturas e fazer reuso em diferentes contextos.
3. Análise → Permite fazer simulações usando resolvedores com restrições (ou
cons-traint solvers) para encontrar alguma instância (com o uso do comando check, que
produz um contraexemplo de uma asserção) ou fazer execuções que satisfazem de-terminadas propriedades (com o comando run, que encontra uma instância de um predicado). Note que um contraexemplo, que é gerado pelo comando check, é equi-valente a um “não existe” e um exemplo (ou modelo), gerado pelo comando run, é equivalente a um “existe”.
Um exemplo extraído do livro de Jackson (2002) pode ilustrar a abordagem. Seja um modelo de sistema de arquivos, que simula um sistema de arquivos do mundo real, onde são apresentadas algumas definições (Listagem 2.1): é criada uma assinatura abstrataFSObject
, que representará cada objeto do sistema de arquivos (linha 1). É abstrata porque a partir dela, as assinaturas concretas, que representarão arquivos (File) ou diretórios (Dir), serão criadas herdando suas características (linhas 2 e 3). Tal entidade FSObject pode ter zero ou um diretório como seu pai ({ parent: lone Dir }). Da mesma forma, a assinaturaRoot
é definida como raiz do diretório do sistema de arquivos porque é o único diretório que não tem qualquer outro objeto como pai (linha 4). Notar que cada diretório tem como conteúdo um conjunto de entidades que podem ser arquivos ou diretórios ({ contents: set FSObject } na linha 2). Em seguida, são definidas três propriedades, onde se assume que sempre são verdadeiras, daí serem definidas como fatos (fact): um diretório é o pai do seu próprio conteúdo (linha 5); cada objeto do sistema de arquivo ou é um arquivo ou um diretório (linha 6); e o ponto de ligação entre os objetos do sistema de arquivos é através do conteúdo (linha 7).
Listagem 2.1 – Sistema de arquivos.
1 a b s t r a c t sig F S O b j e c t { p a r e n t : lone Dir } // Um objeto pode ter 0 ou 1 dir como pai.
2 sig Dir e x t e n d s F S O b j e c t { c o n t e n t s : set F S O b j e c t } // Dir tem seu conteúdo.