Skip to content

Projeto de Elementos de Programação 2018. Simulação de uma epidemia com modelo SEIR.

Notifications You must be signed in to change notification settings

miguelrfernandes/EP-2018

Repository files navigation

Elementos de Programação

Relatório da Primeira Parte do Projeto

Departamento de Matemática, IST 2018/2019

Os ficheiros enviados juntamente com este relatório que contém os módulos são os seguintes:

  • evento_c.py - módulo que disponibiliza o tipo de dados evento
  • cap_c.py - módulo que disponibiliza o tipo de dados cadeia de acontecimentos pendentes
  • individuo_c.py - módulo que disponibiliza o tipo de dados individuo
  • grelha_c.py - módulo que disponibiliza o tipo de dados grelha

evento_c.py

Este módulo disponibiliza o tipo de dados evento. O tipo de dados foi implementado através da definição da classe evento que disponibiliza os seguintes métodos:

  • tempo (): devolve o instante do evento
  • tipo (): devolve o tipo de evento
  • ID (): devolve o identificador do evento

A criação destes objetos é feita fornecendo três parâmetros: o tempo do evento, o tipo de evento (reprodução, deslocamento, avaliação ou morte) e o identificador do objeto (nomeadamente, o identificador do indivíduo ao qual este evento está associado). De seguida, apresentamos alguns exemplos da utilização desta classe:

In [1]: import evento\_c as event

In [1]: a = event.evento(1,'Tipo 1',20) In [2]: b = event.evento(2,'Tipo 2',55) In [3]: e = event.evento(3,'Tipo 1',83) In [4]: a.tempo()

Out [4]: 20

In [5]: b.tipo()

Out [5]: 'Tipo 2'

In [6]: e.ID ()

Out [6]: 3

cap_c.py

Este módulo disponibiliza o tipo de dados cadeia de acontecimentos pendentes. O tipo de dados foi implementado através da definição da classe cap que disponibiliza os seguintes métodos:

  • adicionarE (e): adiciona o evento e à cap
  • proxE (): devolve o evento com menor tempo da cap, caso esta não esteja vazia
  • retirarE (): elimina o evento com menor tempo da cap, caso esta não esteja vazia
  • eliminarID (): remove todos os eventos associados a um certo identificador
  • tamC (): devolve o tamanho da cap
  • mostraC (): mostra o conteúdo da cap

Internamente a CAP funciona como uma lista à qual se adicionam itens da classe evento. Os eventos são ordenados por ordem crescente de tempo. Desta forma, o próximo evento é sempre o primeiro da lista. Para manter a ordem quando se adiciona um evento à CAP, primeiro procura-se o elemento imediatamente anterior ao que se quer introduzir e introduz- se o evento a seguir a este. De forma a aumentar a eficiência, para encontrar este evento usa- se um algoritmo de busca parecido com o exercício feito na aula para a procura de zeros de uma função usando o Teorema de Bolzano-Weierstrass. Este módulo importa o módulo evento_c.py. Cada objeto é criado inicialmente sem eventos (cap vazia). De seguida, apresentamos alguns exemplos da utilização desta classe:

In [1]: import cap\_c as cap

In [2]: c = cap.CAP ()

In [3]: c.adicionarE(a)

In [4]: c.adicionarE(b)

In [5]: c.adicionarE(e)

In [6]: c.proxE()

Out [6]: <evento\_c.evento at 0x10e9e4ef0> In [7]: c.proxE().time()

Out [7]: 20

In [7]: c.mostraC()

ID: 2 Tipo: tipo2 Tempo: 20

ID: 3 Tipo: tipo3 Tempo: 50

ID: 1 Tipo: tipo1 Tempo: 56

In [8]: c.retirarE()

In [9]: c.proxE().time()

Out [9]: 50

In [10]: c.eliminarID(1)

In [11]: c.mostraC()

ID: 3 Tipo: tipo3 Tempo: 50

In [12]: d.tamC() Out [12]: 1

individuo_c.py

Este módulo disponibiliza o tipo de dados individuo. O tipo de dados foi implementado através da definição da classe individuo que disponibiliza os seguintes métodos:

  • estado (): devolve o estado do indivíduo (pode ser S, E, I, R)
  • ID (): devolve o identificador do indivíduo
  • mudae (e): permite alterar o estado de um indivíduo

A criação destes objetos é feita fornecendo dois parâmetros: o estado do indivíduo (suscetível, exposto, infetado ou recuperado) e o identificador do indivíduo. De seguida, apresentamos alguns exemplos da utilização desta classe:**

In [1]: import individuo\_c as individuo In [2]: a = individuo.individuo(1,'S') In [3]: b = individuo.individuo(2,'E') In [4]: a.ID ()

Out [4]: 1

In [5]: a.estado()

Out [5]: 'S'

In [6]: b.estado()

Out [6]: 'E'

In [7]: a.mudae('I')

In [8]: a.estado()

Out [8]: 'I'

grelha_c.py

Este módulo disponibiliza o tipo de dados grelha. O tipo de dados foi implementado através da definição da classe grelha que disponibiliza os seguintes métodos:

  • encontrar (ID): devolve a posição de um indivíduo com um dado identificador
  • obj (p): devolve o objeto na posição p (seja este um indivíduo, obstáculo ou livre)
  • insI (ind,p): adiciona um indivíduo (ind) a uma dada posição (p)
  • delI (p): retira um indivíduo de uma dada posição
  • usaQ (p): devolve True se uma posição não tem obstáculos
  • livQ (p): devolve True se uma posição está livre (=None), e False caso contrário
  • ocuQ (p): devolve True se a posição p se encontra ocupada por um indivíduo, e False caso contrário
  • infQ (p): devolve True se uma posição tiver um indivíduo infetado, e False caso contrário
  • susQ (p): devolve True se uma posição tiver um indivíduo suscetível, e False caso contrário
  • expQ (p): devolve True se uma posição tiver um indivíduo exposto, e False caso contrário
  • recQ (p): devolve True se uma posição tiver um indivíduo recuperado, e False caso contrário
  • lis_Q (c,w): devolve a lista dos elementos de w que verificam o predicado c
  • dentrogrelha(p): assegura que uma coordenada está contida na grelha. Caso não esteja, coloca-a na grelha
  • viz1 (ID): devolve as posições da vizinhança 1 de um indivíduo
  • viz2 (ID): devolve as posições da vizinhança 2 de um indivíduo
  • contactoQ (aID,bp): recebendo o ID de um indivíduo a e a posição de um indivíduo b, devolve True se estes estiverem em contacto, e False caso contrário
  • nInf (): devolve o número total de indivíduos infetados na grelha
  • c_estados (): devolve uma lista de listas com as coordenadas de cada indivíduo em cada estado

Este módulo importa o módulo individuo_c.py. A criação destes objetos é feita fornecendo dois parâmetros: a dimensão N da grelha e a lista de obstáculos. Internamente a grelha funciona como uma matriz: na criação de uma grelha ocupam-se todas as posições com “O” se for um obstáculo ou None caso contrário. De seguida, apresentamos alguns exemplos da utilização desta classe:

In [1]: import individuo\_c as ind In [2]: import grelha\_c as gr

  • Criar a grelha In[3]:g=gr.Grelha(3,[(2,3),(2,2),(2,1),(2,0),(2,-1),(2,-2),(2,-3)])
  • Cria indivíduos para pôr na grelha In [4]: a = ind.individuo(1,'S')

In [5]: b = ind.individuo(2,'S')

In [6]:c = ind.individuo(3,'S')

In [7]: d = ind.individuo(4,'S')

In [8]: e = ind.individuo(5,'S')

In [9]: f = ind.individuo(6,'I')

In [10]: h = ind.individuo(7,'I') In [11]: i = ind.individuo(8,'I') In [12]: j = ind.individuo(9,'I') In [13]: k = ind.individuo(10,'I')

  • Adiciona os indivíduos à grelha In [14]: g.insI(a,[1,2])

In [15]: g.insI(b,[1,-2])

In [16]: g.insI(c,[-1,-2])

In [17]: g.insI(d,[-1,-3])

In [18]: g.insI(e,[-1,3])

In [19]: g.insI(f,[1,3])

In [20]: g.insI(h,[1,1])

In [21]: g.insI(i,[1,-1]) In [22]: g.insI(j,[-1,-1]) In [23]: g.insI(k,[-1,0])

In [24]: g.obj ([1,1])

Out [24]: <individuo\_c.individuo at 0x18153ea5c0> In [25]: g.delI([1,1])

In [26]: g.obj([1,1]) # Como a posição está vazia, não devolve nada In [27]: g.obj([2,1])

Out [27]: 'O'

In [28]: g.encontrar(3)

Out [28]: [-1, -2]

In [29]: g.encontrar(6) Out [29]: [1, 3]

In [30]: g.usaQ([-3,3]) # para posição vazia (=None) Out [30]: True

In [31]: g.usaQ([1,2]) # posição com indivíduo Out [31]: True

In [32]: g.usaQ([2,2]) # posição com obstáculo

Out [32]: False

In [33]: g.livQ([-3,3]) # para posição vazia Out [33]: True

In [34]: g.livQ([1,2]) # posição com indivíduo Out [34]: False

In [35]: g.livQ([2,2]) # posição com obstáculo Out [35]: False

In [36]: g.ocuQ([-3,3]) # para posição vazia Out [36]: False

In [37]: g.ocuQ([1,2]) # posição com indivíduo Out [37]: True

In [38]: g.ocuQ([2,2]) # posição com obstáculo Out [38]: False

In [39]: g.susQ([1,-2]) Out [39]: True

In [40]: g.susQ([1,-1]) Out [40]: False

In [41]: g.infQ([1,-2]) Out [41]: False

In [42]: g.infQ([1,-1]) Out [42]: True

In [43]: g.expQ([1,-1]) Out [43]: False

In [44]: g.recQ([1,-1]) Out [44]: False

In [45]: g.dentrogrelha([4,0]) Out [45]: [-3, 0]

In [46]: g.dentrogrelha([2,0]) Out [46]: [2, 0]

In [47]: g.viz1(a.ID())

Out [47]: [[0, 1], [0, 2], [0, 3], [1, 1], [1, 3], [2, 1], [2, 2], [2, 3]]

In [48]: g.viz1(f.ID())

Out [48]: [[0, 2], [0, 3], [0, -3], [1, 2], [1, -3], [2, 2], [2, 3], [2, -3]]

In [49]: g.viz2(a.ID())

Out [49]:

[[-1, 0],

``[-1, 1],

``[-1, 2],

``[-1, 3],

``[-1, -3],

``[0, 0],

``[0, -3],

``[1, 0],

``[1, -3],

``[2, 0],

``[2, -3],

``[3, 0],

``[3, 1],

``[3, 2],

``[3, 3],

``[3, -3]]

In [50]: g.viz2(f.ID())

Out [50]:

[[-1, 1],

``[-1, 2],

``[-1, 3],

``[-1, -3],

``[-1, -2],

``[0, 1],

``[0, -2],

``[1, 1],

``[1, -2],

``[2, 1],

``[2, -2],

``[3, 1],

``[3, 2],

``[3, 3],

``[3, -3],

``[3, -2]]

In [51]: g.lis\_Q(g.livQ,g.viz1(a.ID())) Out [51]: [[0, 1], [0, 2], [0, 3], [1, 1]]

In [52]: g.lis\_Q(g.livQ,g.viz1(f.ID()))

Out [52]: [[0, 2], [0, 3], [0, -3], [1, -3]]

In [53]: g.contactoQ(a.ID(),g.encontrar(b.ID())) Out [53]: False

In [54]: g.contactoQ(a.ID(),g.encontrar(d.ID())) Out [54]: True

In [55]: g.nInf() Out [55]: 4

In [56]: g.c\_estados()

Out [56]:

[[(-1, 3), (1, 2), (-1, -2), (1, -2), (-1, -3)], [],

``[(1, 3), (-1, 0), (-1, -1), (1, -1)],

``[]]

Elementos de Programação

Relatório da Segunda Parte do Projeto

Departamento de Matemática, IST 2018/2019

Para o simulador decidimos usar os módulos que nós definimos, nomeadamente event_c, cap_c, individuo_c e grelha_c. Os módulos utilizados foram os enviados anteriormente exceto a função eliminaID(ID) da cap. A função foi utilizada foi a seguinte:

def eliminarID(self,ID):

i=len(self.CAP)-1

while i>=0:

if self.CAP[i].ID()==ID:

self.CAP.remove(self.CAP[i]) i=i-1

  • Enviamos em anexo os módulos já corrigidos.
  • Definimos uma variável global novoID, que nos permitiu ir criando novos e diferentes identificadores para cada indivíduo.

Funções

Definimos as seguintes funções:

  • texp(x)
  • novoI(e,p,t)
  • popular(w,Ps,Pi,cen,padj)
  • Pe(x)

texp(m)

Dado um tempo m, calcula um tempo aleatório usando distribuição exponencial. novoID(e,p,t)

Dado um estado, uma posição e o tempo atual da simulação, cria um indivíduo com uma nova identificação e o estado “e”, adiciona o mesmo na posição “p” e adiciona à CAP um evento da deslocação, reprodução, morte e (dependendo do estado) avaliação (eventos “essenciais”) no tempo atual “t” mais o tempo médio de evento aleatório usando distribuição exponencial.

popular(w,Ps, Pi,cen,padj)

Esta função popula a grelha, isto é, acrescenta indivíduos à grelha, dependendo do cenário. Esta distribuição pode ser aleatória (cenário ‘a’) ou agrupar uma percentagem, dada pelo utilizador, de indivíduos em posições adjacentes (cenário ‘b’). O cenário é escolhido através do parâmetro cen da função sim. e a percentagem de individuos em posições adjacentes pelo parâmetro padj

Se o cenário escolhido for o cenário ‘a’, os indivíduos são colocados aleatoriamente na

grelha.

Caso o cenário escolhido seja o cenário ‘b’, o primeiro indivíduo é colocado aleatoriamente na grelha, e os indivíduos seguintes (até a percentagem ser atingida) são adicionados adjacentes ao anterior. Quando este processo está concluído, os restantes indivíduos são adicionados aleatoriamente na grelha (tal como no cenário ‘a’)

Pe(x)

Dado o ID de um individuo, calcula a probabilidade deste passar de suscetivel a exposto. Calcula n1 calculando o tamanho da lista dos individuos infetados da vizinhança 1 do individuo com ID = x e n2 calculando o número de individuos infetados na vizinhança 2 que se encontram em contacto com o individuo com ID = x. Serão estes valores depois a serem aplicados à expressão dada.

While do simulador

Para começar, definimos uma grelha, uma cap, populamos a grelha e define-se o

tempo(=0).

O simulador tem como condição de paragem o tempo limite da simulação e a cap (a simulação acaba quando a cap estiver vazia).

Para cada evento, o simulador verifica o tipo de evento que vai ocorrer (deslocamento, reprodução, morte ou avaliação) e dependendo da probabilidade de cada evento, este é realizado ou não. Caso seja realizado, ocorre como explicado acima para cada evento.

Ao longo do while, é adicionado a uma lista valores que x e y que irão formar o gráfico. O valor de x corresponde ao tempo decorrido, e começa em 0 e vai até Th. O y é o número de infetados na simulação ao longo do tempo. Este valor aumenta cada vez que um indivíduo exposto passa a infetado e diminui de cada vez que um indivíduo infetado morre ou um indivíduo infetado volta a recuperado.

No final do ciclo é definida uma variável “prox” contendo o próximo evento, assim como o tempo do mesmo (para ser avaliado na condição de paragem) e o ID

Tipos de Eventos

Deslocamento

Se o comprimento da lista das posições livres da vizinhança 1 do individuo com ID

  • x for superior a 0 (quando existe posições adjacentes livres), se o comprimento da lista das posições dos indivíduos infetados da vizinhança 1 do individuo com ID = x for superior a 2, este move-se sempre, inserindo na grelha o indivíduo numa dessas posições livres e apagando-o da posição inicial, caso o comprimento da lista das posições dos infetados da vizinhança 1 do individuo com ID = x for inferior a 3, este desloca-se (inserindo na grelha o indivíduo numa dessas posições livres e apagando-o da posição inicial) com probabilidade pd. Quando o evento acaba de ocorrer (havendo ou não deslocação), é criado o próximo evento de deslocação para o indivíduo com ID = x com o tempo de evento igual ao tempo atual mais o tempo médio de deslocação aleatório usando distribuição exponencial e adicionado à CAP.

Reprodução

Se o comprimento da lista das posições livres da vizinhança 1 do individuo com ID

  • x for superior a 0 (quando existe posições adjacentes livres) e se o comprimento da lista das posições ocupadas por indivíduos da vizinhança 1 do individuo com ID = x for superior a 0, este reproduz-se, utilizando a função novoID para criar, adicionar à grelha, criar os eventos “essenciais” e adicioná-os à CAP numa posição aleatória adjacente ao indivíduo com ID = x com probabilidade pr.

Quando o evento acaba de ocorrer (havendo ou não reprodução), é criado o próximo evento de reprodução para o indivíduo com ID = x com o tempo de evento igual ao tempo atual mais o tempo médio de reprodução aleatório usando distribuição exponencial e adicionado à CAP.

Morte

Se o estado do indivíduo com ID = x for “infectado”, este é apagado da grelha e todos os seus eventos na CAP são também eliminados com probabilidade pm mais 10% de pm (pm*1.1) com o máximo de pm = 1. Se no entanto o estado do indivíduo com ID = x não for “infetado”, este apenas é apagado da grelha e todos os seus eventos na CAP são também eliminados com probabilidade pm.

Quando o evento acaba de ocorrer e somente se não houver morte, é criado o próximo evento de morte para o indivíduo com ID = x com o tempo de evento igual ao tempo atual mais o tempo médio de morte aleatório usando distribuição exponencial e adicionado à CAP.

Avaliação

Se o estado do indivíduo com ID = x for “infectado”, este muda o seu estado para “recuperado”

Se o estado do indivíduo com ID = x for “exposto”, este muda o seu estado para “infetado” e é criado o próximo evento de avaliação para o indivíduo com ID = x com o tempo

de evento igual ao tempo atual mais o tempo médio de avaliação “I” aleatório usando distribuição exponencial e adicionado à CAP.

Se o estado do indivíduo com ID = x não for nem “infectado” nem “exposto” (logo, individuo com estado “suscetível”, visto que um indivíduo com estado “recuperado” não está sujeito a avaliação) e o comprimento da lista das posições dos individuos infetados da vizinhança 1 e 2 do individuo com ID = x for superior a 0, este muda o seu estado para “exposto” e é criado o próximo evento de avaliação para o indivíduo com ID = x com o tempo de evento igual ao tempo atual mais o tempo médio de avaliação “E” aleatório usando distribuição exponencial e adicionado à CAP com probabilidade Pe(x). Caso o comprimento da lista das posições dos individuos infetados da vizinhança 1 e 2 do individuo com ID = x for igual a 0 ou não atinge a probabilidade necessária, o seu estado mantém-se “susceptivel” e é criado o próximo evento de avaliação para o indivíduo com ID = x com o tempo de evento igual ao tempo atual mais o tempo médio de avaliação “S” aleatório usando distribuição exponencial e adicionado à CAP.

Criação do ficheiro resultados.txt

É aberto um ficheiro (resultados.txt) onde irá ser adicionada a lista com 4 listas com as coordenadas dos indivíduos susceptíveis, expostos, infectados e recuperados, respectivamente,

correspondendo a evolução da grelha ao longo do tempo.

É escrito na primeira linha a dimensão N da grelha e na segunda a lista de obstáculos.

No inicio de cada ciclo do simulador é definida uma variável “c” boleana com o valor de “False”, e no final de cada evento, se este ocorre (havendo então alteração do aspeto da grelha) esta passa a “True”. Aquando do final do ciclo, se houver de facto alguma alteração, é então escrito no ficheiro uma linha com a lista das 4 listas das posições dos estados. Caso não haja alteração (o evento não se realiza), não escreve, diminuindo assim substancialmente o tamanho do ficheiro.

No final da simulação fecha-se o ficheiro.

Criação do gráfico com a evolução do número de infectados ao longo do tempo

É criada inicialmente aquando da introdução dos individuos iniciais na grelha duas listas (“grafx” e “grafy”), sendo que a primeira representa o tempo e a segunda o número de individuos “infectados” na grelha.

É também definida uma variavel “nInf” que atualiza o número de individuos “infectados” na grelha. Esta começa por ser igual a Pi, e como só existe diminuição do número de individuos infetados (nInf=nInf-1) aquando da morte ou da avaliação (Inf -> Rec) de de um indivíduo infetado e aumento do número de individuos infetados (nInf=nInf+1) aquando da avaliação de (Exp -> Inf) de um indivíduo exposto, só se registam (adicionando a “grafx” o tempo atual e a “grafy” o número atualizado de individuos infetados na grelha) nestas situações.

No final da simulação, cria-se o gráfico com os valores de x cada entrada de “grafx” e os valores de y cada entrada de “grafy”, define-se a dimensão dos eixos e adiciona-se legenda ao gráfico.

Resultados

  • Nota: conseguimos exportar o ficheiro mp4, contudo a qualidade é bastante reduzida, pelo que anexámos uma pasta para cada cenário que contém a animação da simulação cujos gráficos são apresentados abaixo.
  • Os cenários apresentados abaixo foram obtidos usando o simulador com os parâmetros sugeridos no enunciado.

Cenário ‘a’ (ver animação1)

Cenário ‘b’ (ver animação2) (simulação com percentagem 70%)

About

Projeto de Elementos de Programação 2018. Simulação de uma epidemia com modelo SEIR.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published