Quais estratégias de programação posso adotar para modificar facilmente os parâmetros do algoritmo?

17

O desenvolvimento de algoritmos científicos é um processo altamente iterativo, geralmente envolvendo a alteração de muitos parâmetros que desejarei variar como parte do meu projeto experimental ou como parte do aprimoramento do desempenho do algoritmo. Quais estratégias posso adotar para estruturar esses parâmetros para que eu possa alterá-los facilmente entre as iterações e para que eu possa adicionar novos facilmente?

Scottie T
fonte

Respostas:

14

É complicado para o usuário especificar todos os aspectos de um algoritmo. Se o algoritmo permitir componentes aninhados, nenhum número finito de opções seria suficiente. Portanto, é fundamental que as opções não necessariamente "subam" para o nível superior, como no caso de argumentos explícitos ou parâmetros de modelo. Às vezes, isso é chamado de "problema de configuração" na engenharia de software. Acredito que o PETSc tenha um sistema exclusivamente poderoso para gerenciamento de configurações. É semelhante ao padrão Service Locator no ensaio de Martin Fowler sobre inversão de controle .

O sistema de configuração do PETSc funciona através de uma combinação de configuração especificada pelo usuário, gerenciada pelos objetos solucionadores (com consultas get e set) e pelo banco de dados de opções. Qualquer componente da simulação pode declarar uma opção de configuração, um valor padrão e um local para colocar o resultado. Objetos aninhados têm prefixos que podem ser compostos, de modo que todos os objetos que precisam de configuração possam ser endereçados de forma independente. As opções em si podem ser lidas na linha de comando, ambiente, arquivos de configuração ou código. Quando uma opção é declarada, uma cadeia de ajuda e uma página de manual são especificadas, para que a -helpopção seja compreensível e para que uma GUI corretamente vinculada possa ser gravada.

O usuário chama um SetFromOptionsmétodo para fazer com que um objeto se configure com base nas opções da linha de comando. Chamar esta função é opcional e pode não ser chamado se o usuário (pessoa que escreve o código que chama PETSc) estiver expondo as opções por meio de alguma outra interface. É altamente recomendável que o usuário exponha o banco de dados de opções, pois fornece ao usuário final (pessoa que executa o aplicativo) uma grande quantidade de energia, mas isso não é necessário.

Uma configuração típica, chamada via

PetscObjectOptionsBegin(object); /* object has prefix and descriptive string */
PetscOptionsReal("-ts_atol",                                      /* options database key */
                 "Absolute tolerance for local truncation error", /* long description */
                 "TSSetTolerances",                               /* function and man page on topic */
                  ts->atol,                                       /* current/default value *?
                  &ts->atol,                                      /* place to store value */
                  &option_set);                                   /* TRUE if the option was set */
PetscOptionsList("-ts_type","Time stepping method","TSSetType",TSList,
                 defaultType,typeName,sizeof typeName,&option_set);
TSAdaptSetFromOptions(ts->adapt);                                 /* configures adaptive controller method */
/* ... many others */
/* ... the following is only called from implicit implementations */
SNESSetFromOptions(ts->snes);                                     /* configure nonlinear solver. */
PetscOptionsEnd();

Notas:

  • PetscOptionsList()apresenta ao usuário uma opção em uma lista dinâmica. Existe uma arquitetura de plug-in que as novas implementações podem usar para se expor como de primeira classe aos chamadores. (Essas implementações podem ser colocadas em bibliotecas compartilhadas e usadas como primeira classe sem recompilar programas.)
  • SNESSetFromOptions() configura recursivamente os solucionadores lineares, pré-condicionadores e outros componentes que precisam de configuração.
Jed Brown
fonte
11

Eu já enfrentei esse problema várias vezes ao desenvolver meus próprios códigos de simulação do zero: quais parâmetros devem ser inseridos em um arquivo de entrada, quais devem ser retirados da linha de comando etc. Depois de algumas experiências, o seguinte se mostrou eficiente. (Não é tão avançado quanto o PETSc.)

Em vez de escrever um 'programa' de simulação experimental, estou mais inclinado a escrever um pacote Python que contém todas as funções e classes necessárias para executar a simulação. O arquivo de entrada tradicional é substituído pelo pequeno script Python com 5 a 10 linhas de código. Algumas linhas geralmente estão relacionadas ao carregamento de arquivos de dados e à especificação de saída. Outras são instruções para o cálculo real. Bons valores padrão para argumentos opcionais no pacote Python tornam possível para iniciantes usar a biblioteca para simulações simples, enquanto o usuário avançado ainda tem acesso a todos os sinos e assobios.

Alguns exemplos:

Toon Verstraelen
fonte
Isso é ótimo, mas acho que é ortogonal ao problema de configuração. Se você precisar especificar um algoritmo hierárquico ou aninhado, terá opções para especificar para muitos objetos internos. O código que chama esses usuários nem deveria saber sobre sua existência, pois o número de níveis e os tipos de aninhamento podem mudar. Esse é o problema de todas essas opções "borbulhando". Com seu código Python de alto nível, você pode "facilitar" especificar essas opções, mas ainda precisa especificá-las no código. Eu acho que geralmente não é uma coisa boa.
Jed Brown
xmonad usa este método para configurar seu gerenciador de janelas para o X.
rcollyer
2

Como primeiro ponto, eu faria o algoritmo E o software o mais geral possível. Eu aprendi isso da maneira mais difícil.

Digamos que você comece com um caso de teste simples. Você pode fazer isso mais rápido. Mas se você tornou o software muito específico (poucos parâmetros) para este caso inicial, perderá mais e mais tempo adaptando-o toda vez que adicionar um novo grau de liberdade. O que faço agora é gastar mais tempo no começo, tornando a coisa bem geral e aumentando a variação dos parâmetros à medida que avança.

Isso envolve mais testes desde o início, pois você terá mais parâmetros desde o ponto de partida, mas isso significa que você poderá jogar muito com o algoritmo a zero ou a um custo muito baixo.

Exemplo: o algoritmo envolve o cálculo da superfície integral do produto escalar de duas funções vetoriais. Não assuma desde o início o tamanho, a geometria e a discretização da superfície se, no futuro, você quiser mudar isso. Faça uma função de produto pontual, torne a superfície o mais geral possível, calcule a integral de uma maneira formal agradável. Você pode testar cada função que você faz separadamente.

No início, você pode começar a integrar geometrias simples e declarar vários parâmetros no início como constantes. Conforme o tempo passa, se você deseja alterar a geometria, pode fazê-lo facilmente. Se você tivesse feito suposições no início, teria que alterar todo o código todas as vezes.

jbcolmenares
fonte