nodes: 0, module; 1, function_definition; 2, function_name:_get_efron_values_batch; 3, parameters; 4, comment:# pylint: disable=too-many-locals; 5, block; 6, identifier:self; 7, identifier:X; 8, identifier:T; 9, identifier:E; 10, identifier:weights; 11, identifier:beta; 12, expression_statement; 13, expression_statement; 14, expression_statement; 15, expression_statement; 16, expression_statement; 17, comment:# weights = weights[:, None]; 18, comment:# Init risk and tie sums to zero; 19, expression_statement; 20, expression_statement; 21, expression_statement; 22, comment:# counts are sorted by -T; 23, expression_statement; 24, expression_statement; 25, expression_statement; 26, for_statement; 27, return_statement; 28, comment:"""
          Assumes sorted on ascending on T
          Calculates the first and second order vector differentials, with respect to beta.
  
          A good explanation for how Efron handles ties. Consider three of five subjects who fail at the time.
          As it is not known a priori that who is the first to fail, so one-third of
          (φ1 + φ2 + φ3) is adjusted from sum_j^{5} φj after one fails. Similarly two-third
          of (φ1 + φ2 + φ3) is adjusted after first two individuals fail, etc.
  
          Returns
          -------
          hessian: (d, d) numpy array,
          gradient: (1, d) numpy array
          log_likelihood: float
          """; 29, assignment; 30, assignment; 31, assignment; 32, assignment; 33, assignment; 34, assignment; 35, assignment; 36, assignment; 37, assignment; 38, assignment; 39, identifier:count_of_removals; 40, identifier:counts; 41, block; 42, expression_list; 43, pattern_list; 44, attribute; 45, identifier:hessian; 46, call; 47, identifier:gradient; 48, call; 49, identifier:log_lik; 50, integer:0; 51, pattern_list; 52, expression_list; 53, pattern_list; 54, expression_list; 55, pattern_list; 56, expression_list; 57, pattern_list; 58, call; 59, identifier:scores; 60, binary_operator:weights * np.exp(np.dot(X, beta)); 61, identifier:pos; 62, identifier:n; 63, expression_statement; 64, expression_statement; 65, expression_statement; 66, expression_statement; 67, expression_statement; 68, expression_statement; 69, comment:# Calculate sums of Risk set; 70, expression_statement; 71, expression_statement; 72, expression_statement; 73, comment:# Calculate the sums of Tie set; 74, expression_statement; 75, expression_statement; 76, if_statement; 77, expression_statement; 78, expression_statement; 79, expression_statement; 80, expression_statement; 81, expression_statement; 82, if_statement; 83, expression_statement; 84, comment:# This is a batch outer product.; 85, comment:# given a matrix t, for each row, m, compute it's outer product: m.dot(m.T), and stack these new matrices together.; 86, comment:# which would be: np.einsum("Bi, Bj->Bij", t, t); 87, expression_statement; 88, expression_statement; 89, expression_statement; 90, expression_statement; 91, expression_statement; 92, identifier:hessian; 93, identifier:gradient; 94, identifier:log_lik; 95, identifier:n; 96, identifier:d; 97, identifier:X; 98, identifier:shape; 99, attribute; 100, argument_list; 101, attribute; 102, argument_list; 103, identifier:risk_phi; 104, identifier:tie_phi; 105, integer:0; 106, integer:0; 107, identifier:risk_phi_x; 108, identifier:tie_phi_x; 109, call; 110, call; 111, identifier:risk_phi_x_x; 112, identifier:tie_phi_x_x; 113, call; 114, call; 115, identifier:_; 116, identifier:counts; 117, attribute; 118, argument_list; 119, identifier:weights; 120, call; 121, assignment; 122, assignment; 123, assignment; 124, assignment; 125, assignment; 126, assignment; 127, assignment; 128, assignment; 129, assignment; 130, assignment; 131, assignment; 132, comparison_operator:tied_death_counts == 0; 133, comment:# no deaths, can continue; 134, block; 135, assignment; 136, assignment; 137, assignment; 138, assignment; 139, assignment; 140, comparison_operator:tied_death_counts > 1; 141, comment:# a lot of this is now in Einstein notation for performance, but see original "expanded" code here; 142, comment:# https://github.com/CamDavidsonPilon/lifelines/blob/e7056e7817272eb5dff5983556954f56c33301b1/lifelines/fitters/coxph_fitter.py#L755-L789; 143, comment:# it's faster if we can skip computing these when we don't need to.; 144, block; 145, else_clause; 146, assignment; 147, assignment; 148, assignment; 149, assignment; 150, assignment; 151, augmented_assignment; 152, identifier:np; 153, identifier:zeros; 154, tuple; 155, identifier:np; 156, identifier:zeros; 157, tuple; 158, attribute; 159, argument_list; 160, attribute; 161, argument_list; 162, attribute; 163, argument_list; 164, attribute; 165, argument_list; 166, identifier:np; 167, identifier:unique; 168, unary_operator; 169, keyword_argument; 170, attribute; 171, argument_list; 172, identifier:slice_; 173, call; 174, identifier:X_at_t; 175, subscript; 176, identifier:weights_at_t; 177, subscript; 178, identifier:phi_i; 179, subscript; 180, identifier:phi_x_i; 181, binary_operator:phi_i * X_at_t; 182, identifier:phi_x_x_i; 183, call; 184, identifier:risk_phi; 185, binary_operator:risk_phi + array_sum_to_scalar(phi_i); 186, identifier:risk_phi_x; 187, binary_operator:risk_phi_x + matrix_axis_0_sum_to_array(phi_x_i); 188, identifier:risk_phi_x_x; 189, binary_operator:risk_phi_x_x + phi_x_x_i; 190, identifier:deaths; 191, subscript; 192, identifier:tied_death_counts; 193, call; 194, identifier:tied_death_counts; 195, integer:0; 196, expression_statement; 197, continue_statement; 198, identifier:xi_deaths; 199, subscript; 200, identifier:weights_deaths; 201, subscript; 202, identifier:x_death_sum; 203, call; 204, identifier:weight_count; 205, call; 206, identifier:weighted_average; 207, binary_operator:weight_count / tied_death_counts; 208, identifier:tied_death_counts; 209, integer:1; 210, expression_statement; 211, expression_statement; 212, expression_statement; 213, expression_statement; 214, expression_statement; 215, expression_statement; 216, comment:# computes outer products and sums them together.; 217, comment:# Naive approach is to; 218, comment:# 1) broadcast tie_phi_x_x and increasing_proportion into a (tied_death_counts, d, d) matrix; 219, comment:# 2) broadcast risk_phi_x_x and denom into a (tied_death_counts, d, d) matrix; 220, comment:# 3) subtract them, and then sum to (d, d); 221, comment:# Alternatively, we can sum earlier without having to explicitly create (_, d, d) matrices. This is used here.; 222, comment:#; 223, expression_statement; 224, comment:# no tensors here, but do some casting to make it easier in the converging step next.; 225, block; 226, identifier:summand; 227, binary_operator:numer * denom[:, None]; 228, identifier:a2; 229, call; 230, identifier:gradient; 231, binary_operator:gradient + x_death_sum - weighted_average * summand.sum(0); 232, identifier:log_lik; 233, binary_operator:log_lik + np.dot(x_death_sum, beta) + weighted_average * np.log(denom).sum(); 234, identifier:hessian; 235, binary_operator:hessian + weighted_average * (a2 - a1); 236, identifier:pos; 237, identifier:count_of_removals; 238, identifier:d; 239, identifier:d; 240, identifier:d; 241, identifier:np; 242, identifier:zeros; 243, tuple; 244, identifier:np; 245, identifier:zeros; 246, tuple; 247, identifier:np; 248, identifier:zeros; 249, tuple; 250, identifier:np; 251, identifier:zeros; 252, tuple; 253, identifier:T; 254, identifier:return_counts; 255, True; 256, identifier:np; 257, identifier:exp; 258, call; 259, identifier:slice; 260, argument_list; 261, identifier:X; 262, identifier:slice_; 263, identifier:weights; 264, identifier:slice_; 265, identifier:scores; 266, identifier:slice_; 267, None; 268, identifier:phi_i; 269, identifier:X_at_t; 270, attribute; 271, argument_list; 272, identifier:risk_phi; 273, call; 274, identifier:risk_phi_x; 275, call; 276, identifier:risk_phi_x_x; 277, identifier:phi_x_x_i; 278, identifier:E; 279, identifier:slice_; 280, identifier:array_sum_to_scalar; 281, argument_list; 282, augmented_assignment; 283, identifier:X_at_t; 284, identifier:deaths; 285, identifier:weights_at_t; 286, identifier:deaths; 287, identifier:matrix_axis_0_sum_to_array; 288, argument_list; 289, identifier:array_sum_to_scalar; 290, argument_list; 291, identifier:weight_count; 292, identifier:tied_death_counts; 293, assignment; 294, assignment; 295, assignment; 296, assignment; 297, assignment; 298, assignment; 299, assignment; 300, expression_statement; 301, expression_statement; 302, expression_statement; 303, identifier:numer; 304, subscript; 305, attribute; 306, argument_list; 307, binary_operator:gradient + x_death_sum; 308, binary_operator:weighted_average * summand.sum(0); 309, binary_operator:log_lik + np.dot(x_death_sum, beta); 310, binary_operator:weighted_average * np.log(denom).sum(); 311, identifier:hessian; 312, binary_operator:weighted_average * (a2 - a1); 313, identifier:d; 314, identifier:d; 315, identifier:d; 316, identifier:d; 317, identifier:d; 318, identifier:d; 319, attribute; 320, argument_list; 321, binary_operator:pos - count_of_removals; 322, identifier:pos; 323, identifier:np; 324, identifier:dot; 325, attribute; 326, identifier:phi_x_i; 327, identifier:array_sum_to_scalar; 328, argument_list; 329, identifier:matrix_axis_0_sum_to_array; 330, argument_list; 331, call; 332, identifier:pos; 333, identifier:count_of_removals; 334, binary_operator:weights_deaths[:, None] * xi_deaths; 335, identifier:weights_deaths; 336, identifier:tie_phi; 337, call; 338, identifier:tie_phi_x; 339, call; 340, identifier:tie_phi_x_x; 341, call; 342, identifier:increasing_proportion; 343, binary_operator:np.arange(tied_death_counts) / tied_death_counts; 344, identifier:denom; 345, binary_operator:1.0 / (risk_phi - increasing_proportion * tie_phi); 346, identifier:numer; 347, binary_operator:risk_phi_x - np.outer(increasing_proportion, tie_phi_x); 348, identifier:a1; 349, binary_operator:np.einsum("ab,i->ab", risk_phi_x_x, denom) - np.einsum(
                      "ab,i->ab", tie_phi_x_x, increasing_proportion * denom
                  ); 350, assignment; 351, assignment; 352, assignment; 353, identifier:denom; 354, slice; 355, None; 356, attribute; 357, identifier:dot; 358, identifier:summand; 359, identifier:gradient; 360, identifier:x_death_sum; 361, identifier:weighted_average; 362, call; 363, identifier:log_lik; 364, call; 365, identifier:weighted_average; 366, call; 367, identifier:weighted_average; 368, parenthesized_expression; 369, identifier:np; 370, identifier:dot; 371, identifier:X; 372, identifier:beta; 373, identifier:pos; 374, identifier:count_of_removals; 375, identifier:X_at_t; 376, identifier:T; 377, identifier:phi_i; 378, identifier:phi_x_i; 379, attribute; 380, argument_list; 381, subscript; 382, identifier:xi_deaths; 383, identifier:array_sum_to_scalar; 384, argument_list; 385, identifier:matrix_axis_0_sum_to_array; 386, argument_list; 387, attribute; 388, argument_list; 389, call; 390, identifier:tied_death_counts; 391, float:1.0; 392, parenthesized_expression; 393, identifier:risk_phi_x; 394, call; 395, call; 396, call; 397, identifier:denom; 398, binary_operator:1.0 / np.array([risk_phi]); 399, identifier:numer; 400, identifier:risk_phi_x; 401, identifier:a1; 402, binary_operator:risk_phi_x_x * denom; 403, identifier:summand; 404, identifier:T; 405, attribute; 406, argument_list; 407, attribute; 408, argument_list; 409, attribute; 410, argument_list; 411, binary_operator:a2 - a1; 412, identifier:deaths; 413, identifier:astype; 414, identifier:int; 415, identifier:weights_deaths; 416, slice; 417, None; 418, subscript; 419, subscript; 420, identifier:np; 421, identifier:dot; 422, attribute; 423, binary_operator:phi_i[deaths] * xi_deaths; 424, attribute; 425, argument_list; 426, binary_operator:risk_phi - increasing_proportion * tie_phi; 427, attribute; 428, argument_list; 429, attribute; 430, argument_list; 431, attribute; 432, argument_list; 433, float:1.0; 434, call; 435, identifier:risk_phi_x_x; 436, identifier:denom; 437, identifier:summand; 438, identifier:sum; 439, integer:0; 440, identifier:np; 441, identifier:dot; 442, identifier:x_death_sum; 443, identifier:beta; 444, call; 445, identifier:sum; 446, identifier:a2; 447, identifier:a1; 448, identifier:phi_i; 449, identifier:deaths; 450, identifier:phi_x_i; 451, identifier:deaths; 452, identifier:xi_deaths; 453, identifier:T; 454, subscript; 455, identifier:xi_deaths; 456, identifier:np; 457, identifier:arange; 458, identifier:tied_death_counts; 459, identifier:risk_phi; 460, binary_operator:increasing_proportion * tie_phi; 461, identifier:np; 462, identifier:outer; 463, identifier:increasing_proportion; 464, identifier:tie_phi_x; 465, identifier:np; 466, identifier:einsum; 467, string:"ab,i->ab"; 468, identifier:risk_phi_x_x; 469, identifier:denom; 470, identifier:np; 471, identifier:einsum; 472, string:"ab,i->ab"; 473, identifier:tie_phi_x_x; 474, binary_operator:increasing_proportion * denom; 475, attribute; 476, argument_list; 477, attribute; 478, argument_list; 479, identifier:phi_i; 480, identifier:deaths; 481, identifier:increasing_proportion; 482, identifier:tie_phi; 483, identifier:increasing_proportion; 484, identifier:denom; 485, identifier:np; 486, identifier:array; 487, list; 488, identifier:np; 489, identifier:log; 490, identifier:denom; 491, identifier:risk_phi
code: def _get_efron_values_batch(self, X, T, E, weights, beta):  # pylint: disable=too-many-locals
          """
          Assumes sorted on ascending on T
          Calculates the first and second order vector differentials, with respect to beta.
  
          A good explanation for how Efron handles ties. Consider three of five subjects who fail at the time.
          As it is not known a priori that who is the first to fail, so one-third of
          (φ1 + φ2 + φ3) is adjusted from sum_j^{5} φj after one fails. Similarly two-third
          of (φ1 + φ2 + φ3) is adjusted after first two individuals fail, etc.
  
          Returns
          -------
          hessian: (d, d) numpy array,
          gradient: (1, d) numpy array
          log_likelihood: float
          """
  
          n, d = X.shape
          hessian = np.zeros((d, d))
          gradient = np.zeros((d,))
          log_lik = 0
          # weights = weights[:, None]
  
          # Init risk and tie sums to zero
          risk_phi, tie_phi = 0, 0
          risk_phi_x, tie_phi_x = np.zeros((d,)), np.zeros((d,))
          risk_phi_x_x, tie_phi_x_x = np.zeros((d, d)), np.zeros((d, d))
  
          # counts are sorted by -T
          _, counts = np.unique(-T, return_counts=True)
          scores = weights * np.exp(np.dot(X, beta))
          pos = n
  
          for count_of_removals in counts:
  
              slice_ = slice(pos - count_of_removals, pos)
  
              X_at_t = X[slice_]
              weights_at_t = weights[slice_]
  
              phi_i = scores[slice_, None]
              phi_x_i = phi_i * X_at_t
              phi_x_x_i = np.dot(X_at_t.T, phi_x_i)
  
              # Calculate sums of Risk set
              risk_phi = risk_phi + array_sum_to_scalar(phi_i)
              risk_phi_x = risk_phi_x + matrix_axis_0_sum_to_array(phi_x_i)
              risk_phi_x_x = risk_phi_x_x + phi_x_x_i
  
              # Calculate the sums of Tie set
              deaths = E[slice_]
  
              tied_death_counts = array_sum_to_scalar(deaths.astype(int))
              if tied_death_counts == 0:
                  # no deaths, can continue
                  pos -= count_of_removals
                  continue
  
              xi_deaths = X_at_t[deaths]
              weights_deaths = weights_at_t[deaths]
  
              x_death_sum = matrix_axis_0_sum_to_array(weights_deaths[:, None] * xi_deaths)
  
              weight_count = array_sum_to_scalar(weights_deaths)
              weighted_average = weight_count / tied_death_counts
  
              if tied_death_counts > 1:
  
                  # a lot of this is now in Einstein notation for performance, but see original "expanded" code here
                  # https://github.com/CamDavidsonPilon/lifelines/blob/e7056e7817272eb5dff5983556954f56c33301b1/lifelines/fitters/coxph_fitter.py#L755-L789
  
                  # it's faster if we can skip computing these when we don't need to.
                  tie_phi = array_sum_to_scalar(phi_i[deaths])
                  tie_phi_x = matrix_axis_0_sum_to_array(phi_x_i[deaths])
                  tie_phi_x_x = np.dot(xi_deaths.T, phi_i[deaths] * xi_deaths)
  
                  increasing_proportion = np.arange(tied_death_counts) / tied_death_counts
                  denom = 1.0 / (risk_phi - increasing_proportion * tie_phi)
                  numer = risk_phi_x - np.outer(increasing_proportion, tie_phi_x)
  
                  # computes outer products and sums them together.
                  # Naive approach is to
                  # 1) broadcast tie_phi_x_x and increasing_proportion into a (tied_death_counts, d, d) matrix
                  # 2) broadcast risk_phi_x_x and denom into a (tied_death_counts, d, d) matrix
                  # 3) subtract them, and then sum to (d, d)
                  # Alternatively, we can sum earlier without having to explicitly create (_, d, d) matrices. This is used here.
                  #
                  a1 = np.einsum("ab,i->ab", risk_phi_x_x, denom) - np.einsum(
                      "ab,i->ab", tie_phi_x_x, increasing_proportion * denom
                  )
              else:
                  # no tensors here, but do some casting to make it easier in the converging step next.
                  denom = 1.0 / np.array([risk_phi])
                  numer = risk_phi_x
                  a1 = risk_phi_x_x * denom
  
              summand = numer * denom[:, None]
              # This is a batch outer product.
              # given a matrix t, for each row, m, compute it's outer product: m.dot(m.T), and stack these new matrices together.
              # which would be: np.einsum("Bi, Bj->Bij", t, t)
              a2 = summand.T.dot(summand)
  
              gradient = gradient + x_death_sum - weighted_average * summand.sum(0)
              log_lik = log_lik + np.dot(x_death_sum, beta) + weighted_average * np.log(denom).sum()
              hessian = hessian + weighted_average * (a2 - a1)
              pos -= count_of_removals
  
          return hessian, gradient, log_lik