def compute_attachment( xi: List[float], xj: List[float], wij: List[float], start_k: int = 0, eps: float = 0.0, to_opinion: AttachmentType = AttachmentType.TO_INITIAL ) -> Tuple[List[float], List[Dict[Text, int]]]: """Computes the level of attachment to the initial opinion for person i. For person i at time k is computed as follows: a_{i, i}(k) = \frac{x_i(k+1) - x_i(0) + \epsilon}{x_i(k) - x_i(0) + w_{i, j}(k)\bigg(x_j(k) - x_i(k)\bigg) + \epsilon} Args: xi: List of opinions for person i over time (multiple rounds). xj: List of opinions for person j over time (multiple rounds). wij: List of influence from person i to person j. start_k: Start value for k to be 0 or 1. eps: The small amount to always add to the denominator. to_opinion: The type of attachment to either initial or previous. Returns: Value (level) of attachment to the initial opinion or from previous for person i over time, called a_{i, i}. Also note if you want to avoid the possibility of division by zero, you should always use a small epsilon larger than zero to add to the denominator. Raises: ValueError: If the length of opinions were not the same or number of influence weights were not one less than the length of opinion vector. Also if start_k was given anything but 0 or 1. Also if the type of to_opinion was unkown. """ if len(xi) != len(xj): raise ValueError( 'Length of opinions do not match. xi: {}, xj: {}'.format(xi, xj)) if len(xi) != len(wij) + 1: raise ValueError('Length of opinions and influences do not match. ') if start_k != 0 and start_k != 1: raise ValueError( 'Start k should be 0 or 1. It was given {}'.format(start_k)) opinion_len = len(xi) aii_nan_details = [] aii = [] for k in range(start_k, opinion_len - 1): if to_opinion == AttachmentType.TO_INITIAL: xi_k_minus_x0_or_previous_str = 'xi[k]-xi[0]==0' numerator = xi[k + 1] - xi[0] denominator = xi[k] - xi[0] + wij[k] * (xj[k] - xi[k]) + eps elif to_opinion == AttachmentType.TO_PREVIOUS: xi_k_minus_x0_or_previous_str = 'xi[k]-xi[k-1]==0' if k > 0: numerator = xi[k + 1] - xi[k - 1] denominator = xi[k] - xi[k - 1] + wij[k] * (xj[k] - xi[k]) + eps else: numerator = xi[k + 1] - xi[k] denominator = wij[k] * (xj[k] - xi[k]) + eps else: ValueError( 'Type of attachment was unkown. It was {}'.format(to_opinion)) # Getting the details about how NaN is happening. aii_nan_detail = {} if utils.is_almost_zero(numerator) and utils.is_almost_zero( denominator): aii_nan_detail['0/0'] = 1 if not utils.is_almost_zero(numerator) and utils.is_almost_zero( denominator): aii_nan_detail['n/0'] = 1 if utils.is_almost_zero(denominator) and utils.is_almost_zero(xi[k] - xi[0]): aii_nan_detail[xi_k_minus_x0_or_previous_str] = 1 if utils.is_almost_zero(denominator) and utils.is_almost_zero(wij[k]): aii_nan_detail['wij[k]==0'] = 1 if utils.is_almost_zero(denominator) and utils.is_almost_zero(xj[k] - xi[k]): aii_nan_detail['xj[k]-xi[k]==0'] = 1 if utils.is_almost_zero(denominator) and eps > 0: print('Warning: choose a different epsilon.' ' There has been an denominator equals 0' ' with the current one which is {}.'.format(eps)) # attachment = 0 # np.nan << CHECK HERE >> DUE TO NOAH'S CODE IS SET 0. attachment = np.nan if not utils.is_almost_zero(denominator): attachment = numerator / denominator aii.append(attachment) aii_nan_details.append(aii_nan_detail) return aii, aii_nan_details
def test_is_almost_zero_when_should_be_computationally_zero(self): self.assertTrue(utils.is_almost_zero(0.85 - 0.8 + 0.2 * (0.6 - 0.85)))
def test_is_almost_zero_raises_when_negative_num_of_exponents(self): with self.assertRaises(ValueError): utils.is_almost_zero(x=0.1, num_of_exponents=-2)
def test_is_almost_zero_when_exactly_zero(self): self.assertTrue(utils.is_almost_zero(x=0.0, num_of_exponents=10))
def test_is_almost_zero_when_negative_but_small_enough(self): self.assertTrue(utils.is_almost_zero(x=-0.00001, num_of_exponents=4))
def test_is_almost_zero_when_negative_and_not_close_to_zero(self): self.assertFalse(utils.is_almost_zero(x=-1, num_of_exponents=4))
def test_is_almost_zero_when_close_enough_to_zero(self): self.assertTrue(utils.is_almost_zero(x=0.000099, num_of_exponents=4))
def test_is_almost_zero_when_not_close_enough_to_zero(self): self.assertFalse(utils.is_almost_zero(x=0.1, num_of_exponents=4))