From 15fb2d096e76bd13d4eeeb3312d77b07ae16aeb0 Mon Sep 17 00:00:00 2001 From: niklas Date: Sat, 13 Jan 2024 12:55:53 +0100 Subject: [PATCH] Add initial vector_mpi.py --- src/vector_mpi.py | 363 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 src/vector_mpi.py diff --git a/src/vector_mpi.py b/src/vector_mpi.py new file mode 100644 index 0000000..ab24cbf --- /dev/null +++ b/src/vector_mpi.py @@ -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)