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 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 ( 0 < 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 ( 0 < self.rank < self.size - 1 and self.start_idx < 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 0 < 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 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)