//  [python]
def test_step(self, batch, batch_idx):
        # Forward descriptor and basis networks.
        basis_output, desc_output, all_sels = self(batch)

        # Generate indices pairs and obtain data from batch.
        num_views = len(batch[DS.QUANTIZED_COORDS])
        iters_ij, iters_upper_ij = [], []

        for view_i in range(num_views):
            for view_j in range(num_views):
                if view_i == view_j:
                    continue
                if view_i < view_j:
                    iters_upper_ij.append((view_i, view_j))
                iters_ij.append((view_i, view_j))

        # sub_pc is a subset of the full point cloud, selected by ME quantization
        full_pc, sub_pc = {}, {}
        for view_i in range(num_views):
            full_pc[view_i] = batch[DS.PC][view_i][0]
            sub_pc[view_i] = full_pc[view_i][all_sels[view_i]]

        # Basis pre-conditioning for scale normalization.
        normalized_basis, basis_multiplier = {}, {}
        for view_i in range(num_views):
            basis_origin = basis_output.features_at(view_i)
            svd_res = torch.linalg.svd(basis_origin)
            right_multiplier = torch.diag_embed(svd_res.S) @ svd_res.V.transpose(-1, -2)
            normalized_basis[view_i], basis_multiplier[view_i] = svd_res.U, right_multiplier

        # Get descriptor
        sub_desc = {}
        for view_i in range(num_views):
            sub_desc[view_i] = desc_output.features_at(view_i)

        # Compute pairwise feature descriptors phi_k^kl, phi_l^kl for each pair
        phi_i_all, phi_j_all = {}, {}
        for (view_i, view_j) in iters_upper_ij:
            phi_i_all[(view_i, view_j)], phi_j_all[(view_i, view_j)] = self.basis_net.align_basis_via_pd_test(
                normalized_basis[view_i], normalized_basis[view_j],
                sub_desc[view_i], sub_desc[view_j],
                thres=self.hparams.gpd_thres
            )
            phi_i_all[(view_j, view_i)], phi_j_all[(view_j, view_i)] = \
                phi_j_all[(view_i, view_j)], phi_i_all[(view_i, view_j)]

        # Computes initial maps.
        maps_init = {}
        robust_kernel, robust_iter = self.basis_net.get_robust_kernel()
        for (view_i, view_j) in iters_ij:
            maps_init[(view_i, view_j)] = self.basis_net.solve_optimal_maps(
                phi_i_all[(view_i, view_j)], phi_j_all[(view_i, view_j)],
                robust_kernel=robust_kernel, num_iter=robust_iter
            )

        # Optimize maps via synchronization (Sec.5).
        maps_optimized = self.optimize_map(maps_init, phi_i_all, phi_j_all)

        # Generate flow.
        final_flows = {}
        for (view_i, view_j) in iters_ij:
            final_flows[(view_i, view_j)] = self.basis_net.compute_flow_from_maps(
                normalized_basis[view_i], normalized_basis[view_j], maps_optimized[(view_i, view_j)],
                sub_pc[view_i], sub_pc[view_j], pcond_multiplier=basis_multiplier[view_j],
                sparse_coords=batch[DS.QUANTIZED_COORDS][view_i][0][0]
            )['final']

        # Measure errors.
        full_final_flows = self.propagte_to_full_flow(batch, final_flows)
        error = self.evaluate_flow_error(batch, full_final_flows)

        return full_final_flows, error