Skip to content

Commit

Permalink
feat(appunti): Computational Intelligence Baionetta (#22)
Browse files Browse the repository at this point in the history
Aggiunti appunti per l'esame di Computational Intelligence 🤓
  • Loading branch information
ncvescera committed Aug 17, 2023
2 parents c3e1e13 + 6ebb7c8 commit 5757ae3
Show file tree
Hide file tree
Showing 146 changed files with 5,003 additions and 0 deletions.
3,671 changes: 3,671 additions & 0 deletions magistrale/Anno 1/Computational Intelligence/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import numpy as np

class Problem_tsp:
def __init__(self, nc, mat):
self.ncities = nc
self.dmat = mat # Matrice che contiene le distanze

def create_random_instance(nc):
# Crea due vettori x e y che contengono le coordinate di ciascuna città nel range [-50,50]
x = 100 * np.random.random(nc) - 50
y = 100 * np.random.random(nc) - 50
m = np.zeros((nc, nc))
for i in range(nc):
for j in range(nc):
m[i,j] = np.sqrt((x[i]-x[j])**2+(y[i]-y[j])**2)
return Problem_tsp(nc,m)

# l è l'elenco dei nodi visitati, includendo il primo e l'ultimo (che sono uguali)
def objective_function(self, l):
s = 0
for i in range(self.ncities):
c1 = l[i]
c2 = l[i+1]
d = self.dmat[c1,c2]
s = s+d
return s

def do_2_opt(l, i, j):
l1 = l[:i+1]
l2 = l[i+1:j]
l3 = l[j:]
return l1+l2[::-1]+l3

# Compute the difference on the objective function if the 2-opt operation is performed
def delta_2_opt(self, l, i, j):
return -self.dmat[l[i], l[i+1]] - self.dmat[l[j-1], l[j]] + self.dmat[l[i], l[j-1]] + self.dmat[l[i+1], l[j]]

def local_search(self, init_sol=None, verbose=False):
n = self.ncities
if init_sol is None:
# Creazione di una soluzione casuale
x = list(range(1, n))
np.random.shuffle(x)
x = [0]+x+[0]
else:
x = init_sol.copy()
improved = True
fx = self.objective_function(x)
if verbose:
print("Initial value {}".format(fx))
while improved:
best_delta = 1e300 # Numero grande per i confronti seguenti
# Il delta sarebbe quanto aumenta la funzione obiettivo se io faccio la 2-opt (l'obiettivo è averlo più basso possibile)
for i in range(1, n-1): # Controllo cosa succede se applico 2-opt senza effettivamente modificare x (lo cambio solo dopo aver trovato la migliore configurazione possibile)
for j in range(i+3, n):
delta = self.delta_2_opt(x, i, j)
if delta < best_delta:
i_best = i
j_best = j
best_delta = delta
if best_delta < 0:
fx = fx + best_delta
x = Problem_tsp.do_2_opt(x, i_best, j_best)
improved = True
if verbose:
print("New value {}".format(fx))
else:
improved = False
return x, fx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from TSP import *
p = Problem_tsp.create_random_instance(50)
print(p.ncities)
print(p.dmat)

l = list(range(0, 50))
l.append(0)
print(len(l))

print(p.objective_function(l))

l1 = Problem_tsp.do_2_opt(l, 22, 44)
print(p.objective_function(l1))

delta = p.delta_2_opt(l, 22, 44)
print(delta)
print(p.objective_function(l)+delta)

p = Problem_tsp.create_random_instance(20)
print(p.local_search(verbose=True))
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import pgmpy
from pgmpy.models import BayesianNetwork
from pgmpy.factors.discrete import TabularCPD
from pgmpy.inference import VariableElimination
from pgmpy.inference import ApproxInference

net = BayesianNetwork([('a', 'c'), ('b', 'c')])

print(net.nodes)
print(net.edges)

pa = TabularCPD('a', 2, [[0.2], [0.8]])
pb = TabularCPD('b', 2, [[0.4], [0.6]])
pc = TabularCPD('c', 2, [[0.2, 0.3, 0.1, 0.15], [0.8, 0.7, 0.9, 0.85]], ['a', 'b'], [2, 2])
net.add_cpds(pa, pb, pc)

inference = VariableElimination(net)

rl = inference.query(variables = ['c'], evidence = {'a':0, 'b':0})
print(rl.values)

rl2 = inference.query(variables = ['c'], evidence = {'a':0})
print(rl2.values)

rl3 = inference.query(variables = ['c'])
print(rl3.values)

inference2 = ApproxInference(net)
rl_2 = inference2.query(variables=['c'])
print(rl_2.values)
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Una semplice implementazione dell'algoritmo Differential Evolution
import numpy as np

class Objective_Function:
def __init__(self, fun, dim, domains):
'''
- fun è la definzione della funzione
- dim è il numero di parametri
- domains è la lista degli intervalli [x_i_min, x_i_max] per ogni variabile x_i
'''
self.fun = fun
self.dim = dim
self.domains = domains

def __call__(self, x):
return self.fun(x)


class Differential_Evolution:
def __init__(self, objf, np, f, cr, max_gen):
self.objf = objf
self.np = np
self.f = f
self.cr = cr
self.max_gen = max_gen

def initialize(self): # Serve ad inizializzare l'algoritmo (Ad esempio è necessario creare i vettori della popolazione)
d = self.objf.dim
self.population = []
self.values = []
for i in range(self.np):
r = np.random.random(d)
for j in range(d):
l, u = self.objf.domains[j] # intervallo della j-esima variabile della funzione
r[j] = l + (u - l) * r[j]
self.population.append(r)
self.values.append(self.objf(r))
self.best_f = 1e300
self.find_best()

def find_best(self):# trovo il miglior elemento della popolazione
self.i_best = 0
for i in range(1, self.np):
if self.values[i] < self.values[self.i_best]:
self.i_best = i
if self.values[self.i_best] < self.best_f:
self.best_f = self.values[self.i_best]
self.best = self.population[self.i_best]
print("found new best with f = {}".format(self.best_f))

def evolution(self):
self.initialize()
for g in range(1, self.max_gen+1):
mutants = self.differential_mutation()
trials = self.crossover(mutants) # gli elementi prodotti dal crossover nel differential evolution si chiamano trials
self.selection(trials)
self.find_best()
return self.best_f, self.best

def differential_mutation(self):
mutants = []
for i in range(self.np):
# RAND/1 implementations
l = [j for j in range(self.np) if j != i]
r1, r2, r3 = np.random.choice(l, 3, replace=False)
m = self.population[r1] + self.f * (self.population[r2] - self.population[r3])
mutants.append(m)
return mutants

def crossover(self, mutants):
trials = []
d = self.objf.dim
for i in range(self.np):
j_rand = np.random.randint(0, d)
tr = np.zeros(d)
for j in range(d):
if np.random.random() < self.cr or j == j_rand:
tr[j] = mutants[i][j]
else:
tr[j] = self.population[i][j]
trials.append(tr)
return trials

def selection(self, trials):
for i in range(self.np):
fx = self.objf(trials[i])
if fx < self.values[i]:
self.population[i] = trials[i]
self.values[i] = fx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from diffevol import *

'''
objf = Objective_Function(lambda x: np.sqrt(x[0]**2+x[1]**2), 2, [[-1,1], [-1,1]])
print(objf(np.array([1,1])))
a = Differential_Evolution(objf, 20, 0.9, 1, 100)
a.evolution()
'''

# Provando ad allargare il dominio tra -10 e 10
objf = Objective_Function(lambda x: np.sqrt(x[0]**2+x[1]**2), 2, [[-10,10], [-10,10]])
objf(np.array([1,1]))
a = Differential_Evolution(objf, 20, 0.9, 1, 100)
a.evolution()

# è possibile giocare con i parametri in vari modi
objf = Objective_Function(lambda x: np.sqrt(np.dot(x,x)), 10, [[-10,10] for i in range(10)])
a = Differential_Evolution(objf, 50, 0.9, 0.5, 1000)
a.evolution()
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# A simple genetic algorithm for unconstrained binary maximization problems
import numpy as np

class Binary_genetic_algorithm:

def __init__(self, problem, num_elem=None, num_gen=100, pcross=0.9, pmut=0.01):
self.problem=problem
self.num_bits=problem.get_dim()
if num_elem is None:
self.num_elem=self.num_bits
else:
self.num_elem=num_elem
self.pcross=pcross
self.pmut=pmut
self.num_gen=num_gen

def run(self):
self.init_population()
for gen in range(0,self.num_gen):
mating_pool=self.select_mating_pool()
children=self.do_crossover(mating_pool)
self.do_mutation(children)
self.select_new_population(children)
return self.best, self.best_f

def init_population(self):
self.population=[]
self.f_obj=np.zeros(self.num_elem)
self.best=None
self.best_f=-1
for i in range(0,self.num_elem):
ind=np.random.randint(0,1+1,self.num_bits)
self.population.append(ind)
self.f_obj[i]=self.problem.objective_function(ind)
self.update_best(ind,self.f_obj[i])

def update_best(self, x, fx):
if fx>self.best_f:
self.best_f=fx
self.best=x
print("new best ",fx)

def select_mating_pool(self):
mating_pool=[]
for i in range(0,self.num_elem//2):
p1=self.roulette_wheel()
p2=self.roulette_wheel()
mating_pool.append((p1,p2))
return mating_pool

def roulette_wheel(self):
s=np.sum(self.f_obj)
r=np.random.random()*s
i=0
while r>s:
r=r-self.f_obj[i]
i=i+1
return self.population[i]

def do_crossover(self, mating_pool):
children=[]
for p1, p2 in mating_pool:
if np.random.random()<self.pcross:
c1, c2 = self.crossover_operator(p1,p2)
else:
c1=p1.copy()
c2=p2.copy()
children.append(c1)
children.append(c2)
return children

def crossover_operator(self, p1, p2):
# one point crossover
l1=list(p1)
l2=list(p2)
j=np.random.randint(1,self.num_bits)
c1=np.array(l1[:j]+l2[j:])
c2=np.array(l2[:j]+l1[j:])
return c1,c2

def do_mutation(self,children):
for c in children:
for i in range(0, self.num_bits):
if np.random.random()<self.pmut:
c[i]=1-c[i]

def select_new_population(self,children):
# Find the best among the children and the parents
f_child=np.array([self.problem.objective_function(c) for c in children])
ib1=np.argmax(self.f_obj)
ib2=np.argmax(f_child)
# First case: the best child is better than the the best parent
if f_child[ib2]>self.f_obj[ib1]:
self.population=children
self.f_obj=f_child
self.update_best(children[ib2],f_child[ib2])
else:
iw=np.argmin(f_child)
children[iw]=self.population[ib1]
f_child[iw]=self.f_obj[ib1]
self.population=children
self.f_obj=f_child
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Un'istanza è data dal numero dei nodi numerati da 0 al numero di nodi -1 (0, . . ., num_nodes-1) -> se ho 6 nodi sono numerati da 0 a 5
# È data anche dalla lista degli archi (una coppia di nodi)

import numpy as np

class Maxcut_problem:

def __init__(self, num_nodes, edges):
self.num_nodes = num_nodes
self.edges = edges

def create_random_instance(num_nodes, edge_prob):
edges=[]
for i in range(0,num_nodes):
for j in range(i+1,num_nodes):
if np.random.random()<edge_prob:
edges.append((i,j))
return Maxcut_problem(num_nodes,edges)

def objective_function(self,c):
# c è un vettore di n-bit
# c is a num_nodes binary string
num_cuts = 0
for x,y in self.edges:
if c[x]!=c[y]:
num_cuts +=1
return num_cuts

def get_dim(self):
return self.num_nodes
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from binary_genetic_algorithm import *
from maxcut import *

p = Maxcut_problem.create_random_instance(20, 0.1)
g = Binary_genetic_algorithm(p, num_elem=20)
g.run()
print(len(p.edges))


p2 = Maxcut_problem.create_random_instance(40, 0.2)
print(len(p2.edges))
g = Binary_genetic_algorithm(p2, num_elem=20)
g.run()
Loading

0 comments on commit 5757ae3

Please sign in to comment.