(self, covmat, rets=None)
return res
Perform forward pass.

        Parameters
        ----------
        covmat : torch.Tensor
            Covariance matrix of shape `(n_samples, n_assets, n_assets)`.

        rets : torch.Tensor or None
            If tensor then of shape `(n_samples, n_assets)` representing expected returns. If provided triggers
            computation of maximum share ratio. Else None triggers computation of minimum variance portfolio.

        Returns
        -------
        weights : torch.Tensor
            Of shape (n_samples, n_assets) representing the optimal weights. If `rets` provided, then
            maximum sharpe ratio portfolio (tangency portfolio) used both on intra and inter cluster level. Otherwise
            minimum variance portfolio.

        Notes
        -----
        Currently there is not batching over the sample dimension - simple for loop is used.
Perform forward pass.
Perform forward pass .
def forward(self, covmat, rets=None):
        """Perform forward pass.

        Parameters
        ----------
        covmat : torch.Tensor
            Covariance matrix of shape `(n_samples, n_assets, n_assets)`.

        rets : torch.Tensor or None
            If tensor then of shape `(n_samples, n_assets)` representing expected returns. If provided triggers
            computation of maximum share ratio. Else None triggers computation of minimum variance portfolio.

        Returns
        -------
        weights : torch.Tensor
            Of shape (n_samples, n_assets) representing the optimal weights. If `rets` provided, then
            maximum sharpe ratio portfolio (tangency portfolio) used both on intra and inter cluster level. Otherwise
            minimum variance portfolio.

        Notes
        -----
        Currently there is not batching over the sample dimension - simple for loop is used.

        """
        n_samples, n_assets, _ = covmat.shape
        dtype, device = covmat.dtype, covmat.device

        corrmat = Cov2Corr()(covmat)

        w_l = []  # we need to iterate over the sample dimension (currently no speedup)

        for i in range(n_samples):
            cluster_ixs, cluster_centers = self.kmeans_layer(corrmat[i])

            w_intra_clusters = torch.zeros((n_assets, self.n_clusters), dtype=dtype, device=device)

            for c in range(self.n_clusters):
                in_cluster = torch.where(cluster_ixs == c)[0]  # indices from the same cluster
                intra_covmat = covmat[[i]].index_select(1, in_cluster).index_select(2, in_cluster)  # (1, ?, ?)
                intra_rets = None if rets is None else rets[[i]].index_select(1, in_cluster)  # (1, ?)
                w_intra_clusters[in_cluster, c] = self.analytical_markowitz_layer(intra_covmat, intra_rets)[0]

            inter_covmat = w_intra_clusters.T @ (covmat[i] @ w_intra_clusters)  # (n_clusters, n_clusters)
            inter_rets = None if rets is None else (w_intra_clusters.T @ rets[i]).view(1, -1)  # (1, n_clusters)
            w_inter_clusters = self.analytical_markowitz_layer(inter_covmat.view(1, self.n_clusters, self.n_clusters),
                                                               inter_rets)  # (1, n_clusters)
            w_final = (w_intra_clusters * w_inter_clusters).sum(dim=1)  # (n_assets,)

            w_l.append(w_final)

        res = torch.stack(w_l, dim=0)

        return res
def forward ( self , covmat , rets = None ) : n_samples , n_assets , _ = covmat . shape dtype , device = covmat . dtype , covmat . device corrmat = Cov2Corr ( ) ( covmat ) w_l = [ ] # we need to iterate over the sample dimension (currently no speedup) for i in range ( n_samples ) : cluster_ixs , cluster_centers = self . kmeans_layer ( corrmat [ i ] ) w_intra_clusters = torch . zeros ( ( n_assets , self . n_clusters ) , dtype = dtype , device = device ) for c in range ( self . n_clusters ) : in_cluster = torch . where ( cluster_ixs == c ) [ 0 ] # indices from the same cluster intra_covmat = covmat [ [ i ] ] . index_select ( 1 , in_cluster ) . index_select ( 2 , in_cluster ) # (1, ?, ?) intra_rets = None if rets is None else rets [ [ i ] ] . index_select ( 1 , in_cluster ) # (1, ?) w_intra_clusters [ in_cluster , c ] = self . analytical_markowitz_layer ( intra_covmat , intra_rets ) [ 0 ] inter_covmat = w_intra_clusters . T @ ( covmat [ i ] @ w_intra_clusters ) # (n_clusters, n_clusters) inter_rets = None if rets is None else ( w_intra_clusters . T @ rets [ i ] ) . view ( 1 , - 1 ) # (1, n_clusters) w_inter_clusters = self . analytical_markowitz_layer ( inter_covmat . view ( 1 , self . n_clusters , self . n_clusters ) , inter_rets ) # (1, n_clusters) w_final = ( w_intra_clusters * w_inter_clusters ) . sum ( dim = 1 ) # (n_assets,) w_l . append ( w_final ) res = torch . stack ( w_l , dim = 0 ) return res