1
0

Add initial vector_mpi.py

This commit is contained in:
Niklas Birk 2024-01-13 12:55:53 +01:00
parent 0450811b0c
commit 15fb2d096e

363
src/vector_mpi.py Normal file
View File

@ -0,0 +1,363 @@
import numpy as np
from mpi4py import MPI
class Vector:
start_idx = 0 # Nullter Eintrag des Vektors auf dem aktuellen Rang
end_idx = 0 # Letzer Eintrag des Vektors auf dem aktuellen Rang
rank_size = 0 # Dimension des Vektors der auf dem aktuellen Rang gespeichert wird
kind = '' # Art des Vektors, Zeilen oder Spaltenvektor
vec = np.arange(rank_size)
vshape = np.arange(2) # Array mit Länge 2, um die Shape des Vektors zu speichern
dim = 0 # Gesamtdimension des Vektors, Länge des Vektors
comm = MPI.COMM_WORLD
size = comm.Get_size()
rank = comm.Get_rank()
# Konstruktor
def __init__(self, array):
if isinstance(array, np.ndarray):
form = array.shape
if len(array) < self.size:
raise ValueError("ERROR_3: Die Dimension des Vektors ist kleiner als die Anzahl der benutzten Ränge.")
if len(form) > 2:
raise ValueError("ERROR_2: Falsche Dimension, kann kein 1 x n oder n x 1 Vektor sein.")
# Array ist Zeilenvektor:
if len(form) == 1 or (len(form) == 2 and form[0] == 1):
self.vshape[0] = 1
self.vshape[1] = len(array) # Shape des Vectors
self.start_idx = int(self.rank * len(array) / self.size)
self.end_idx = int(len(array) / self.size + self.rank * len(array) / self.size) - 1
self.rank_size = (
self.end_idx - self.start_idx) + 1 # Größe des Teilvektors auf dem akt. Rang: Differenz zw. Start- und Endindex + 1
self.vec = array[
self.start_idx: self.end_idx + 1] # Auf jedem Rang werden die Einträge vom Start bis zum Endindex gespeichert
self.kind = 'row'
self.dim = len(array)
# Array ist Spaltenvektor
if len(form) == 2 and form[1] == 1:
self.vshape[0] = len(array)
self.vshape[1] = 1
self.start_idx = int(self.rank * len(array) / self.size)
self.end_idx = int(len(array) / self.size + self.rank * len(array) / self.size) - 1
self.rank_size = (
self.end_idx - self.start_idx) + 1 # Größe des Teilvektors auf dem akt. Rang: Differenz zw. Start- und Endindex + 1
self.vec = array[
self.start_idx: self.end_idx + 1] # Auf jedem Rang werden die Einträge vom Start bis zum Endindex gespeichert
self.kind = 'column'
self.dim = len(array)
elif isinstance(array, list):
self.vshape[0] = 1
self.vshape[1] = len(array)
self.start_idx = int(self.rank * len(array) / self.size)
self.end_idx = int(len(array) / self.size + self.rank * len(array) / self.size) - 1
self.rank_size = (self.end_idx - self.start_idx) + 1
self.vec = np.array(array[self.start_idx:self.end_idx + 1])
self.kind = 'row'
self.dim = len(array)
else:
raise ValueError(
"ERROR_1: Die übergebene Variable ist kein Numpy-Array, Keine Initialisierung der Vector-Klasse möglich.")
def __add__(self, other): # Überschreibung der Addition
if (isinstance(self, Vector) and isinstance(other, Vector)):
Add_Vec = Vector(np.arange(self.dim))
if (self.vshape[0] == other.vshape[0] and self.vshape[1] == other.vshape[1]):
for i in range(0, self.rank_size):
Add_Vec.vec[i] = self.vec[i] + other.vec[i]
else:
raise ValueError("Die Dimensionen der Vektoren stimmen nicht überein, Addition nicht möglich.")
elif (isinstance(self, Vector) and isinstance(other, (int, float, complex))):
Add_Vec = Vector(np.arange(self.dim))
for i in range(0, self.rank_size):
Add_Vec.vec[i] = self.vec[i] + other
elif (isinstance(self, (int, float, complex)) and isinstance(other, Vector)):
Add_Vec = Vector(np.arange(other.dim))
for i in range(0, other.rank_size):
Add_Vec.vec[i] = other.vec[i] + self
else:
raise ValueError("Ungeeigneter Datentyp für die Addition mit einem Vektor.")
return Add_Vec
def __radd__(self, other): # Überschreibung der Addition eines Vektors von rechts
Add_Vec = Vector(np.arange(self.dim))
if (isinstance(self, Vector) and isinstance(other, (int, float, complex))):
Add_Vec = Vector(np.arange(self.dim))
for i in range(0, self.rank_size):
Add_Vec.vec[i] = self.vec[i] + other
else:
raise ValueError("Ungeeigneter Datentyp für die Addition mit einem Vektor.")
return Add_Vec
def __sub__(self, other): # Überschreibung der Subtraktion
if (isinstance(self, Vector) and isinstance(other, Vector)):
Sub_Vec = Vector(np.arange(self.dim))
if (self.vshape[0] == other.vshape[0] and self.vshape[1] == other.vshape[1]):
for i in range(0, self.rank_size):
Sub_Vec.vec[i] = self.vec[i] - other.vec[i]
else:
raise ValueError("Die Dimension der Vektoren stimmen nicht überein, Subtraktion nicht möglich.")
elif (isinstance(self, Vector) and isinstance(other, (int, float, complex))):
Sub_Vec = Vector(np.arange(self.dim))
for i in range(0, self.rank_size):
Sub_Vec.vec[i] = self.vec[i] - other
elif (isinstance(self, (int, float, complex)) and isinstance(other, Vector)):
Sub_Vec = Vector(np.arange(self.dim))
for i in range(0, other.rank_size):
Sub_Vec.vec[i] = other.vec[i] - self
else:
raise ValueError("Ungeeigneter Datentyp für die Subtraktion mit einem Vektor.")
return Sub_Vec
def __rsub__(self, other): # Subtraktion einer Zahl von einem Vektor
Sub_Vec = Vector(np.arange(self.dim))
if (isinstance(self, Vector) and isinstance(other, (float, int, complex))):
for i in range(0, self.rank_size):
Sub_Vec.vec[i] = self.vec[i] - other
else:
raise ValueError("Ungeeigneter Datentyp für die Subtraktion von einem Vektor.")
return Sub_Vec
def __mul__(self, other): # Überschreibung der Multiplikation
if (isinstance(self, Vector) and isinstance(other, Vector)):
Mult_Vec = Vector(np.arange(self.dim))
if (self.vshape[0] == other.vshape[0] and self.vshape[1] == other.vshape[
1]): # Elementweise Multiplikation
for i in range(0, self.rank_size):
Mult_Vec.vec[i] = self.vec[i] * other.vec[i]
elif (self.vshape[1] == other.vshape[0] and self.vshape[0] == 1): # Inneres Produkt (Skalarprodukt)
skal_prod = 0
for i in range(0, self.rank_size):
skal_prod = skal_prod + self.vec[i] * other.vec[i]
return skal_prod
elif (self.vshape[0] == other.vshape[1] and self.vshape[1] == 1):
raise ValueError("Kann erst implementiert werden, wenn Matrix-Klasse existiert.")
else:
raise ValueError("Die Dimensionen der Vektoren stimmen nicht überein, Multiplikation nicht möglich.")
elif (isinstance(self, Vector) and isinstance(other, (int, float, complex))):
Mult_Vec = Vector(np.arange(self.dim))
for i in range(0, self.rank_size):
Mult_Vec.vec[i] = self.vec[i] * other
elif (isinstance(self, (int, float, complex)) and isinstance(other, Vector)):
Mult_Vec = Vector(np.arange(self.dim))
for i in range(0, other.rank_size):
Mult_Vec.vec[i] = other.vec[i] * self
else:
raise ValueError("Ungeeigneter Datentyp für die Multiplikation mit einem Vektor.")
return Mult_Vec
def __rmul__(self, other): # Rechtsseitige Multiplikation von einer Zahl an einen Vektor
Mult_Vec = Vector(np.arange(self.dim))
if (isinstance(self, Vector) and isinstance(other, (int, float, complex))):
for i in range(0, self.rank_size):
Mult_Vec.vec[i] = self.vec[i] * other
else:
raise ValueError("Ungeeigneter Datentyp für die Multiplikation mit einem Vektor.")
return Mult_Vec
def __truediv__(self, other):
Div_Vec = Vector(np.arange(self.dim, dtype=np.double))
if (isinstance(self, Vector) and isinstance(other, Vector)):
if (self.vshape[0] == other.vshape[0] and self.vshape[1] == other.vshape[1]):
for i in range(0, self.rank_size):
if (other.vec[i] == 0):
raise ValueError("Ein Eintrag des Divisor-Vektors ist 0, Divion nicht möglich.")
Div_Vec.vec[i] = self.vec[i] / other.vec[i]
else:
raise ValueError("Die Dimensionen der Vektoren stimmen nicht überein, Division nicht möglich.")
elif (isinstance(self, (int, float, complex)) and isinstance(other, Vector)):
for i in range(0, other.rank_size):
if (other.vec[i] == 0):
raise ValueError("Ein Eintrag des Divisor-Vektors ist 0, Divion nicht möglich.")
Div_Vec.vec[i] = self / other.vec[i]
elif (isinstance(self, Vector) and isinstance(other, (float, int, complex))):
if (other == 0):
raise ValueError("Division durch Null ist nicht möglich.")
else:
for i in range(0, self.rank_size):
Div_Vec.vec[i] = self.vec[i] / other
else:
raise ValueError("ERROR 11: Ungeeigneter Datentyp für die Division mit einem Vektor.")
return Div_Vec
def __rtruediv__(self, other):
Div_Vec = Vector(np.arange(self.dim, dtype=np.double))
if (isinstance(self, Vector) and isinstance(other, (float, int, complex))):
if (other == 0):
raise ValueError("Division durch Null ist nicht möglich.")
else:
for i in range(0, self.rank_size):
Div_Vec.vec[i] = self.vec[i] / other
else:
raise ValueError("ERROR 10: Uneignete Datentyp, um einen Vektor durch diesen zu dividieren")
return Div_Vec
def __neg__(self):
Neg_Vec = -1 * self
return Neg_Vec
def shape(self):
if (self.rank == 0):
return self.vshape
def T(self):
Transpose = self
if (self.kind == 'row'):
Transpose.kind = 'column'
else:
Transpose.kind = 'row'
# Tauschen der Dimensionen
var_shift = self.vshape[0]
Transpose.vshape[0] = self.vshape[1]
Transpose.vshape[1] = var_shift
return Transpose
def str(self): # Rückgabe des gesamten Vektors als string
str_rep = ''
if (self.rank == 0):
if (self.kind == 'row'):
str_rep = '[' + ','.join(map(str, self.vec))
if (self.kind == 'column'):
str_rep = '[' + '\n'.join(map(str, self.vec))
if (self.size > 1):
self.comm.send(str_rep, dest=self.rank + 1)
elif (self.rank == self.size - 1):
if (self.kind == 'row'):
str_rep = self.comm.recv(source=self.rank - 1) + ',' + ','.join(map(str, self.vec))
if (self.kind == 'column'):
str_rep = self.comm.recv(source=self.rank - 1) + '\n' + '\n'.join(map(str, self.vec))
else:
if (self.kind == 'row'):
str_rep = self.comm.recv(source=self.rank - 1) + ',' + ','.join(map(str, self.vec))
if (self.kind == 'column'):
str_rep = self.comm.recv(source=self.rank - 1) + '\n' + '\n'.join(map(str, self.vec))
self.comm.send(str_rep, dest=self.rank + 1)
str_rep = self.comm.bcast(str_rep, root=self.size - 1)
if (self.rank == 0):
return str_rep + ']'
def string(self, limit_entry): # Gibt den Vektor als String zurück bis zum Eintrag limit_entry
str_rep = ''
if (limit_entry > self.vec_size):
raise ValueError("ERROR_4: Die eingegebene Zahl ist größer, als der größte Index des Vectors.")
# Rank 0
if (self.rank == 0 and limit_entry <= self.end_idx): # Limit_entry befindet sich im Rang 0
if (self.kind == 'row'):
str_rep = '[' + ','.join(map(str, self.vec[:limit_entry]))
if (self.kind == 'column'):
str_rep = '[' + '\n'.join(map(str, self.vec[:limit_entry]))
if (self.size > 1):
self.comm.send(str_rep, dest=self.rank + 1)
if (self.rank == 0 and limit_entry > self.end_idx): # Limit_entry befindet sich nicht im Rang 0
if (self.kind == 'row'):
str_rep = '[' + ','.join(map(str, self.vec))
if (self.kind == 'column'):
str_rep = '[' + '\n'.join(map(str, self.vec))
if (self.size > 1):
self.comm.send(str_rep, dest=self.rank + 1)
# Rank im Intervall [1,size-1]
if (
self.rank > 0 and self.rank < self.size - 1 and limit_entry <= self.start_idx): # wenn lim_ent == start_idx, dann wurden bereits alle relevanten Indizes im String gespeichert, da Vector nullinitialisiert ist
str_rep = self.comm.recv(source=self.rank - 1)
self.comm.send(str_rep, dest=self.rank + 1)
if (
self.rank > 0 and self.rank < self.size - 1 and limit_entry > self.start_idx and limit_entry <= self.end_idx):
if (self.kind == 'row'):
str_rep = self.comm.recv(source=self.rank - 1) + ',' + ','.join(
map(str, self.vec[:(limit_entry - self.start_idx)]))
if (self.kind == 'column'):
str_rep = self.comm.recv(source=self.rank - 1) + '\n' + '\n'.join(
map(str, self.vec[:(limit_entry - self.start_idx)])) + ']'
self.comm.send(str_rep, dest=self.rank + 1)
if (self.rank > 0 and self.rank < self.size - 1 and limit_entry > self.end_idx):
if (self.kind == 'row'):
str_rep = self.comm.recv(source=self.rank - 1) + ',' + ','.join(map(str, self.vec))
if (self.kind == 'column'):
str_rep = self.comm.recv(source=self.rank - 1) + '\n' + '\n'.join(map(str, self.vec))
self.comm.send(str_rep, dest=self.rank + 1)
# Rank size-1
if (self.rank == self.size - 1 and limit_entry <= self.start_idx and self.rank > 1):
str_rep = self.comm.recv(source=self.rank - 1)
if (self.rank == self.size - 1 and limit_entry >= self.start_idx and self.rank > 1):
if (self.kind == 'row'):
str_rep = self.comm.recv(source=self.rank - 1) + ',' + ','.join(
map(str, self.vec[:(limit_entry - self.start_idx)]))
if (self.kind == 'column'):
str_rep = self.comm.recv(source=self.rank - 1) + '\n' + '\n'.join(
map(str, self.vec[:(limit_entry - self.start_idx)]))
str_rep = self.comm.bcast(str_rep, root=self.size - 1)
if (self.rank == 0):
return str_rep + ']'
def norm(self): # Berechnung der 2-Norm / euklidischen Norm
norm = 0
sum_of_squares = 0
if (self.rank == 0):
for i in range(0, self.rank_size):
sum_of_squares = sum_of_squares + self.vec[i] ** 2
if (self.size > 1):
self.comm.send(sum_of_squares, dest=self.rank + 1)
elif (self.rank == self.size - 1):
sum_of_squares = self.comm.recv(source=self.rank - 1)
for i in range(0, self.rank_size):
sum_of_squares = sum_of_squares + self.vec[i] ** 2
else:
sum_of_squares = self.comm.recv(source=self.rank - 1)
for i in range(0, self.rank_size):
sum_of_squares = sum_of_squares + self.vec[i] ** 2
self.comm.send(sum_of_squares, dest=self.rank + 1)
sum_of_squares = self.comm.bcast(sum_of_squares, root=self.size - 1)
norm = np.sqrt(sum_of_squares)
return norm
def normalize(self): # Normalisierung eines Vectors
norm = self.norm()
if (norm == 0):
return self
normalized_vec = self / norm
return normalized_vec
# Main-Funktion
x = Vector(np.arange(10))
print(x.str(), x.shape())
print(x.vshape[0], x.vshape[1])
minus = -1 * x
_x = -x
print(_x.str())
print(minus.str())
y = Vector(2 * np.arange(10))
print(y.str())
z = x - y
print(z.str())
ae = x + 5
print(ae.str())
o = x * y
print(o.str())
a = Vector(np.array([[1], [2]]))
b = Vector(np.array([1, 2]))
print(a.shape())
# c = a * b
# print(c.vec)