@@ -228,16 +228,16 @@ def _compute_w(self, covarF_inv, covarFB, meanF, wB):
228228 g1 = np .dot (np .dot (onesF .T , covarF_inv ), meanF )
229229 g2 = np .dot (np .dot (onesF .T , covarF_inv ), onesF )
230230 if wB is None :
231- g_result = - self .ls [- 1 ] * g1 / g2 + 1 / g2
232- g , w1 = float ( g_result . item () if hasattr ( g_result , 'item' ) else g_result ), 0
231+ g = - self .ls [- 1 ] * g1 / g2 + 1 / g2
232+ w1 = 0
233233 else :
234234 onesB = np .ones (wB .shape )
235235 g3 = np .dot (onesB .T , wB )
236236 g4 = np .dot (covarF_inv , covarFB )
237237 w1 = np .dot (g4 , wB )
238238 g4 = np .dot (onesF .T , w1 )
239- g_result = - self .ls [- 1 ] * g1 / g2 + (1 - g3 + g4 ) / g2
240- g = float (g_result . item () if hasattr ( g_result , 'item' ) else g_result )
239+ g = - self .ls [- 1 ] * g1 / g2 + (1 - g3 + g4 ) / g2
240+ g = float (g [ 0 , 0 ] )
241241 # 2) compute weights
242242 w2 = np .dot (covarF_inv , onesF )
243243 w3 = np .dot (covarF_inv , meanF )
@@ -259,16 +259,16 @@ def _compute_lambda(self, covarF_inv, covarFB, meanF, wB, i, bi):
259259 # 3) Lambda
260260 if wB is None :
261261 # All free assets
262- result = (c4 [i ] - c1 * bi ) / c
263- return float (result .item () if hasattr (result , 'item' ) else result ), bi
262+ res = (c4 [i ] - c1 * bi ) / c
264263 else :
265264 onesB = np .ones (wB .shape )
266265 l1 = np .dot (onesB .T , wB )
267266 l2 = np .dot (covarF_inv , covarFB )
268267 l3 = np .dot (l2 , wB )
269268 l2 = np .dot (onesF .T , l3 )
270- result = ((1 - l1 + l2 ) * c4 [i ] - c1 * (bi + l3 [i ])) / c
271- return float (result .item () if hasattr (result , 'item' ) else result ), bi
269+ res = ((1 - l1 + l2 ) * c4 [i ] - c1 * (bi + l3 [i ])) / c
270+ res = float (res [0 , 0 ])
271+ return res , bi
272272
273273 def _get_matrices (self , f ):
274274 # Slice covarF,covarFB,covarB,meanF,meanB,wF,wB
@@ -288,18 +288,25 @@ def _diff_lists(list1, list2):
288288
289289 @staticmethod
290290 def _reduce_matrix (matrix , listX , listY ):
291- # Reduce a matrix to the provided list of rows and columns
291+ """
292+ Extract a submatrix from the given matrix using specified row and column indices.
293+
294+ Uses numpy advanced indexing with np.ix_ for vectorized selection,
295+ which is significantly faster than the previous nested loop implementation
296+ for large matrices.
297+
298+ :param matrix: input matrix to extract submatrix from
299+ :type matrix: np.ndarray
300+ :param listX: row indices to select
301+ :type listX: list
302+ :param listY: column indices to select
303+ :type listY: list
304+ :return: submatrix with selected rows and columns, or None if indices are empty
305+ :rtype: np.ndarray or None
306+ """
292307 if len (listX ) == 0 or len (listY ) == 0 :
293- return
294- matrix_ = matrix [:, listY [0 ] : listY [0 ] + 1 ]
295- for i in listY [1 :]:
296- a = matrix [:, i : i + 1 ]
297- matrix_ = np .append (matrix_ , a , 1 )
298- matrix__ = matrix_ [listX [0 ] : listX [0 ] + 1 , :]
299- for i in listX [1 :]:
300- a = matrix_ [i : i + 1 , :]
301- matrix__ = np .append (matrix__ , a , 0 )
302- return matrix__
308+ return None
309+ return matrix [np .ix_ (listX , listY )]
303310
304311 def _purge_num_err (self , tol ):
305312 # Purge violations of inequality constraints (associated with ill-conditioned cov matrix)
0 commit comments