Source code for pyifdm.methods.codas.ifs

# Copyright (c) 2022 Jakub Więckowski

import numpy as np

[docs] def ifs(matrix, weights, types, normalization, distance_1, distance_2, tau): """ Calculates the alternatives preferences based on Intuitionistic Fuzzy Sets Parameters ---------- matrix : ndarray Decision matrix / alternatives data. Alternatives are in rows and Criteria are in columns. weights : ndarray Vector of criteria weights in a crisp or Intuitionistic Fuzzy form types : ndarray Types of criteria, 1 profit, -1 cost normalization: callable Function used to calculate normalized decision matrix distance_1: callable Function used to calculate distance between two IFS distance_2: callable Function used to calculate distance between two IFS tau: float Threshold parameter Returns ------- ndarray Crisp preferences of alternatives """ def _psi(ifs, tau=tau): # tau from 0.01 to 0.05 if np.abs(ifs) >= tau: return 1 return 0 def calculate_distance(method, wmatrix, Am, f): """ Calculates the distance between two Intuitionistic Fuzzy Sets (u, v) using given distance measure Parameters ---------- method : callable Function used to calculate distance wmatrix : ndarray Weighted intuitionistic fuzzy matrix Am : ndarray Negative ideal solution f : float Multiplication factor Returns ------- float Crisp value representing distance """ if method.__name__ == 'normalized_euclidean_distance': return np.sqrt(f * np.sum([method(wmatrix[j], Am[j]) for j in range(wmatrix.shape[0])])) elif method.__name__ == 'normalized_hamming_distance': return f * np.sum([method(wmatrix[j], Am[j]) for j in range(wmatrix.shape[0])]) else: return np.sum([method(wmatrix[j], Am[j]) for j in range(wmatrix.shape[0])]) # normalized matrix nmatrix = normalization(matrix, types) # weighted normalized matrix wmatrix = np.zeros((nmatrix.shape[0], nmatrix.shape[1], 3), dtype=object) # transform crisp weights to ifs if weights.ndim == 1: weights = np.repeat(weights, 2).reshape((len(weights), 2)) wmatrix[:, :, 0] = nmatrix[:, :, 0] * weights[:, 0] wmatrix[:, :, 1] = nmatrix[:, :, 1] + weights[:, 1] - nmatrix[:, :, 1] * weights[:, 1] wmatrix[:, :, 2] = 1 - wmatrix[:, :, 0] - wmatrix[:, :, 1] # negative ideal solution Am = np.zeros((wmatrix.shape[1], 3), dtype=object) # profit criteria Am[types==1, 0] = np.min(wmatrix[:, types==1, 0], axis=0) Am[types==1, 1] = np.max(wmatrix[:, types==1, 1], axis=0) Am[types==1, 2] = 1 - Am[types==1, 0] - Am[types==1, 1] # cost criteria Am[types==-1, 0] = np.max(wmatrix[:, types==-1, 0], axis=0) Am[types==-1, 1] = np.min(wmatrix[:, types==-1, 1], axis=0) Am[types==-1, 2] = 1 - Am[types==-1, 0] - Am[types==-1, 1] f1, f2 = 1, 1 if 'normalized' in distance_1.__name__: f1 = 1/(2*matrix.shape[1]) if 'normalized' in distance_2.__name__: f2 = 1/(2*matrix.shape[1]) # distances D1, D2 = np.zeros((wmatrix.shape[0], 1), dtype=object), np.zeros((wmatrix.shape[0], 1), dtype=object) for i in range(wmatrix.shape[0]): D1[i] = calculate_distance(distance_1, wmatrix[i], Am, f1) D2[i] = calculate_distance(distance_2, wmatrix[i], Am, f2) # relative assessment matrix RA = np.zeros((matrix.shape[0], matrix.shape[0])) for i in range(RA.shape[0]): for j in range(RA.shape[1]): RA[i, j] = (D1[i] - D1[j]) + \ (_psi(D1[i] - D1[j]) * (D2[i] - D2[j])) # assessment score AS = np.sum(RA, axis=1) return AS