jiahuimbzuai/CoQuIR huggingface dataset:
//  [python]
def cluster(adata:anndata.AnnData,method:str='leiden',
            use_rep:str='X_pca',random_state:int=1024,
            n_components=None, **kwargs):
    
    if method=='leiden':
        sc.tl.leiden(adata,**kwargs)
    elif method=='louvain':
        sc.tl.louvain(adata,**kwargs)
    elif method=='GMM':
        if n_components is None:
            print('You need to input the `n_components` when methods is `GMM`')
            return
        print(f"""running GaussianMixture clustering""")
        data=adata.obsm[use_rep].copy()
        ini = get_initial_means(data,n_components, 'k-means++', 0)
        gmm = mixture.GaussianMixture(n_components = n_components,random_state=random_state,
                     means_init=ini, **kwargs)
        gmm.fit(data)
        adata.obs['gmm_cluster']=gmm.predict(data)
        adata.obs['gmm_cluster']=adata.obs['gmm_cluster'].astype(str)
        
        #new_num=adata.obs['gmm_cluster'].value_counts()[adata.obs['gmm_cluster'].value_counts()>10].shape[0]
        #adata.obs.loc[adata.obs['gmm_cluster'].isin(adata.obs['gmm_cluster'].value_counts()[adata.obs['gmm_cluster'].value_counts()<10].index.tolist()),'gmm_cluster']='-1'
        
        #adata.obs['gmm_cluster']=adata.obs['gmm_cluster'].astype('category')
        #adata.obs['gmm_cluster'].cat.categories=pd.Index(list(range(len(adata.obs['gmm_cluster'].cat.categories))))
        
        print(f"""finished: found {n_components} clusters and added
    'gmm_cluster', the cluster labels (adata.obs, categorical)""")
    elif method=='schist':
        try:
            import schist
        except ImportError:
            raise ImportError(
                'Please install the schist using conda `conda install -c conda-forge schist` \nor `pip install git+https://github.com/dawe/schist.git`'
            )
        schist.inference.nested_model(adata, **kwargs)
//  [python]
def cluster(adata:anndata.AnnData,method:str='leiden',
            use_rep:str='X_pca',random_state:int=1024,
            n_components=None, **kwargs):
    
    if method=='leiden':
        sc.tl.leiden(adata,**kwargs)
    elif method=='louvain':
        sc.tl.louvain(adata,**kwargs)
    elif method=='GMM':
        if n_components is None:
            print('You need to input the `n_components` when methods is `GMM`')
            return
        print(f"""running GaussianMixture clustering""")
        data=adata.obsm[use_rep].copy()
        ini = get_initial_means(data,n_components, 'k-means++', 0)
        gmm = GaussianMixture(n_components = n_components,random_state=random_state,
                     means_init=ini, **kwargs)
        gmm.fit(data)
        adata.obs['gmm_cluster']=gmm.predict(data)
        adata.obs['gmm_cluster']=adata.obs['gmm_cluster'].astype(str)
        
        #new_num=adata.obs['gmm_cluster'].value_counts()[adata.obs['gmm_cluster'].value_counts()>10].shape[0]
        #adata.obs.loc[adata.obs['gmm_cluster'].isin(adata.obs['gmm_cluster'].value_counts()[adata.obs['gmm_cluster'].value_counts()<10].index.tolist()),'gmm_cluster']='-1'
        
        #adata.obs['gmm_cluster']=adata.obs['gmm_cluster'].astype('category')
        #adata.obs['gmm_cluster'].cat.categories=pd.Index(list(range(len(adata.obs['gmm_cluster'].cat.categories))))
        
        print(f"""finished: found {n_components} clusters and added
    'gmm_cluster', the cluster labels (adata.obs, categorical)""")
    elif method=='schist':
        try:
            import schist
        except ImportError:
            raise ImportError(
                'Please install the schist using conda `conda install -c conda-forge schist` \nor `pip install git+https://github.com/dawe/schist.git`'
            )
        schist.inference.nested_model(adata, **kwargs)
//  [python]
def test_gaussian_mixture_diag(self):
        data = load_iris()
        X = data.data
        model = mixture.GaussianMixture(n_components=2, covariance_type="diag")
        model.fit(X)
        model_onnx = convert_sklearn(
            model,
            "GM",
            [("input", DoubleTensorType([None, 4]))],
            target_opset=TARGET_OPSET,
        )
        self.assertIn("ReduceLogSumExp", str(model_onnx))
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(
            X.astype(np.float64)[40:60],
            model,
            model_onnx,
            basename="GaussianMixtureC2DiagDouble",
            intermediate_steps=False,
        )
        self._test_score(model, X, TARGET_OPSET, decimal=4)
//  [python]
def test_gaussian_mixture_diag(self):
        data = load_iris()
        X = data.data
        model = GaussianMixture(n_components=2, covariance_type="diag")
        model.fit(X)
        model_onnx = convert_sklearn(
            model,
            "GM",
            [("input", DoubleTensorType([None, 4]))],
            target_opset=TARGET_OPSET,
        )
        self.assertIn("ReduceLogSumExp", str(model_onnx))
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(
            X.astype(np.float64)[40:60],
            model,
            model_onnx,
            basename="GaussianMixtureC2DiagDouble",
            intermediate_steps=False,
        )
        self._test_score(model, X, TARGET_OPSET, decimal=4)
//  [python]
def test_gaussian_mixture_spherical(self):
        data = load_iris()
        X = data.data
        model = mixture.GaussianMixture(n_components=2, covariance_type="spherical")
        model.fit(X)
        model_onnx = convert_sklearn(
            model,
            "GM",
            [("input", DoubleTensorType([None, 4]))],
            target_opset=TARGET_OPSET,
        )
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(
            X.astype(np.float64)[40:60],
            model,
            model_onnx,
            basename="GaussianMixtureC2SphericalDouble",
            intermediate_steps=False,
        )
        self._test_score(model, X, TARGET_OPSET, decimal=4)
//  [python]
def test_gaussian_mixture_spherical(self):
        data = load_iris()
        X = data.data
        model = GaussianMixture(n_components=2, covariance_type="spherical")
        model.fit(X)
        model_onnx = convert_sklearn(
            model,
            "GM",
            [("input", DoubleTensorType([None, 4]))],
            target_opset=TARGET_OPSET,
        )
        self.assertIsNotNone(model_onnx)
        dump_data_and_model(
            X.astype(np.float64)[40:60],
            model,
            model_onnx,
            basename="GaussianMixtureC2SphericalDouble",
            intermediate_steps=False,
        )
        self._test_score(model, X, TARGET_OPSET, decimal=4)
//  [python]
def _test_gaussian_mixture_full_black_op(self):
        data = load_iris()
        X = data.data
        model = mixture.GaussianMixture(n_components=2, covariance_type="full")
        model.fit(X)
        with self.assertRaises(RuntimeError):
            convert_sklearn(
                model,
                "GM",
                [("input", DoubleTensorType([None, 4]))],
                target_opset=TARGET_OPSET,
                black_op={"Add"},
            )
        model_onnx = convert_sklearn(
            model,
            "GM",
            [("input", DoubleTensorType([None, 4]))],
            target_opset=TARGET_OPSET,
            black_op={"ReduceLogSumExp"},
        )
        self.assertIsNotNone(model_onnx)
        self.assertNotIn("ReduceLogSumExp", str(model_onnx))
        dump_data_and_model(
            X.astype(np.float64)[40:60],
            model,
            model_onnx,
            basename="GaussianMixtureC2FullBLDouble",
            intermediate_steps=False,
        )
        self._test_score(model, X, TARGET_OPSET)
//  [python]
def _test_gaussian_mixture_full_black_op(self):
        data = load_iris()
        X = data.data
        model = GaussianMixture(n_components=2, covariance_type="full")
        model.fit(X)
        with self.assertRaises(RuntimeError):
            convert_sklearn(
                model,
                "GM",
                [("input", DoubleTensorType([None, 4]))],
                target_opset=TARGET_OPSET,
                black_op={"Add"},
            )
        model_onnx = convert_sklearn(
            model,
            "GM",
            [("input", DoubleTensorType([None, 4]))],
            target_opset=TARGET_OPSET,
            black_op={"ReduceLogSumExp"},
        )
        self.assertIsNotNone(model_onnx)
        self.assertNotIn("ReduceLogSumExp", str(model_onnx))
        dump_data_and_model(
            X.astype(np.float64)[40:60],
            model,
            model_onnx,
            basename="GaussianMixtureC2FullBLDouble",
            intermediate_steps=False,
        )
        self._test_score(model, X, TARGET_OPSET)
//  [python]
def _test_gaussian_mixture_full_black_op_noargmax(self):
        data = load_iris()
        X = data.data
        model = mixture.GaussianMixture(n_components=2, covariance_type="full")
        model.fit(X)
        with self.assertRaises(RuntimeError):
            convert_sklearn(
                model,
                "GM",
                [("input", DoubleTensorType([None, 4]))],
                target_opset=TARGET_OPSET,
                black_op={"Add"},
            )
        model_onnx = convert_sklearn(
            model,
            "GM",
            [("input", DoubleTensorType([None, 4]))],
            target_opset=TARGET_OPSET,
            black_op={"ReduceLogSumExp", "ArgMax"},
        )
        self.assertIsNotNone(model_onnx)
        self.assertNotIn("ArgMax", str(model_onnx))
        dump_data_and_model(
            X.astype(np.float64)[40:60],
            model,
            model_onnx,
            basename="GaussianMixtureC2FullBLNMDouble",
            intermediate_steps=False,
        )
        self._test_score(model, X, TARGET_OPSET)
//  [python]
def _test_gaussian_mixture_full_black_op_noargmax(self):
        data = load_iris()
        X = data.data
        model = GaussianMixture(n_components=2, covariance_type="full")
        model.fit(X)
        with self.assertRaises(RuntimeError):
            convert_sklearn(
                model,
                "GM",
                [("input", DoubleTensorType([None, 4]))],
                target_opset=TARGET_OPSET,
                black_op={"Add"},
            )
        model_onnx = convert_sklearn(
            model,
            "GM",
            [("input", DoubleTensorType([None, 4]))],
            target_opset=TARGET_OPSET,
            black_op={"ReduceLogSumExp", "ArgMax"},
        )
        self.assertIsNotNone(model_onnx)
        self.assertNotIn("ArgMax", str(model_onnx))
        dump_data_and_model(
            X.astype(np.float64)[40:60],
            model,
            model_onnx,
            basename="GaussianMixtureC2FullBLNMDouble",
            intermediate_steps=False,
        )
        self._test_score(model, X, TARGET_OPSET)
//  [python]
def fit_gmm(sar_ds, n_components=4):
    """Wrapper around sklearn's GaussianMixture().fit() method to automatically feed
    an xarray Dataset with specific properties instead of an np.array.
    
    """
    #prepare the sar data in a format that suits the model
    gm_input = _sklearn_flatten(sar_ds)
    
    #pad the list with zeros to force a cluster at or near (0,0) [i.e. open water]
    #gm_input = np.concatenate((gm_input,[[0,0]]*1500))
    
    #fit the model to the data and return the fitted model
    return mixture.GaussianMixture(n_components=n_components).fit(gm_input)
//  [python]
def fit_gmm(sar_ds, n_components=4):
    """Wrapper around sklearn's GaussianMixture().fit() method to automatically feed
    an xarray Dataset with specific properties instead of an np.array.
    
    """
    #prepare the sar data in a format that suits the model
    gm_input = _sklearn_flatten(sar_ds)
    
    #pad the list with zeros to force a cluster at or near (0,0) [i.e. open water]
    #gm_input = np.concatenate((gm_input,[[0,0]]*1500))
    
    #fit the model to the data and return the fitted model
    return GaussianMixture(n_components=n_components).fit(gm_input)
//  [python]
def trainGMM(data, clusterNum):
    """
    训练混合高斯模型
    """
    model = mixture.GaussianMixture(n_components=clusterNum, covariance_type='full')
    model.fit(data)
    return model
//  [python]
def trainGMM(data, clusterNum):
    """
    训练混合高斯模型
    """
    model = GaussianMixture(n_components=clusterNum, covariance_type='full')
    model.fit(data)
    return model
//  [python]
def GMM(self, name='GMM.png', save=False):
        '''
        Function to perform the GMM clustering algorithm
        '''

        if not self.do_mDoppler:
            raise OptionIsFalseError("do_mDoppler")

        # Get the first image
        img = self.mDoppler[0].T

        # Reshape the image
        img = img.reshape((img.shape[0] * img.shape[1], 1))

        # Perform GMM
        gmm = mixture.GaussianMixture(n_components=3, random_state=0).fit(img)

        # Create an empty array
        labels = np.zeros_like(gmm.predict(img))
        labels[gmm.predict(img) != -1] = 1

        # Reshape the labels
        labels = labels.reshape((self.mDoppler[0].shape[0], self.mDoppler[0].shape[1]))

        # Plot the results
        fig, ax = plt.subplots(nrows=2, figsize=(9,7))

        ax[0].contourf(self.mDoppler[0], cmap='viridis')
        ax[0].set_title("Original")

        ax[1].contourf(labels, cmap='viridis')
        ax[1].set_title("GMM")

        fig.tight_layout()
        plt.show()

        if save:
            fig.savefig(os.path.join(figures_dir, name))
//  [python]
def GMM(self, name='GMM.png', save=False):
        '''
        Function to perform the GMM clustering algorithm
        '''

        if not self.do_mDoppler:
            raise OptionIsFalseError("do_mDoppler")

        # Get the first image
        img = self.mDoppler[0].T

        # Reshape the image
        img = img.reshape((img.shape[0] * img.shape[1], 1))

        # Perform GMM
        gmm = GaussianMixture(n_components=3, random_state=0).fit(img)

        # Create an empty array
        labels = np.zeros_like(gmm.predict(img))
        labels[gmm.predict(img) != -1] = 1

        # Reshape the labels
        labels = labels.reshape((self.mDoppler[0].shape[0], self.mDoppler[0].shape[1]))

        # Plot the results
        fig, ax = plt.subplots(nrows=2, figsize=(9,7))

        ax[0].contourf(self.mDoppler[0], cmap='viridis')
        ax[0].set_title("Original")

        ax[1].contourf(labels, cmap='viridis')
        ax[1].set_title("GMM")

        fig.tight_layout()
        plt.show()

        if save:
            fig.savefig(os.path.join(figures_dir, name))
//  [python]
def test_gaussian_mixture_full_black_op_noargmax_inf(self):
        data = load_iris()
        X = data.data
        model = mixture.GaussianMixture(n_components=10, covariance_type="full")
        model.fit(X)
        model_onnx1 = convert_sklearn(
            model,
            "GM",
            [("input", DoubleTensorType([None, 4]))],
            target_opset=TARGET_OPSET,
            options={id(model): {"score_samples": True}},
        )
        model_onnx2 = convert_sklearn(
            model,
            "GM",
            [("input", DoubleTensorType([None, 4]))],
            target_opset=TARGET_OPSET,
            options={id(model): {"score_samples": True}},
            black_op={"ReduceLogSumExp", "ArgMax"},
        )
        self.assertNotIn("ArgMax", str(model_onnx2))
        sess1 = InferenceSession(
            model_onnx1.SerializeToString(), providers=["CPUExecutionProvider"]
        )
        res1 = sess1.run(None, {"input": (X[:5] * 1e2).astype(np.float64)})
        a1, b1, c1 = res1

        sess2 = InferenceSession(
            model_onnx2.SerializeToString(), providers=["CPUExecutionProvider"]
        )
        res2 = sess2.run(None, {"input": (X[:5] * 1e2).astype(np.float64)})
        a2, b2, c2 = res2

        self.assertEqual(b1.max(), b2.max())
        self.assertEqual(b1.min(), b2.min())
        self.assertLess(abs(c1.max() - c2.max()) / c2.max(), 1e-5)
        self.assertLess(abs(c1.min() - c2.min()) / c2.min(), 1e-5)

        self._test_score(
            model, X, TARGET_OPSET, black_op={"ReduceLogSumExp", "ArgMax"}, decimal=2
        )
//  [python]
def test_gaussian_mixture_full_black_op_noargmax_inf(self):
        data = load_iris()
        X = data.data
        model = GaussianMixture(n_components=10, covariance_type="full")
        model.fit(X)
        model_onnx1 = convert_sklearn(
            model,
            "GM",
            [("input", DoubleTensorType([None, 4]))],
            target_opset=TARGET_OPSET,
            options={id(model): {"score_samples": True}},
        )
        model_onnx2 = convert_sklearn(
            model,
            "GM",
            [("input", DoubleTensorType([None, 4]))],
            target_opset=TARGET_OPSET,
            options={id(model): {"score_samples": True}},
            black_op={"ReduceLogSumExp", "ArgMax"},
        )
        self.assertNotIn("ArgMax", str(model_onnx2))
        sess1 = InferenceSession(
            model_onnx1.SerializeToString(), providers=["CPUExecutionProvider"]
        )
        res1 = sess1.run(None, {"input": (X[:5] * 1e2).astype(np.float64)})
        a1, b1, c1 = res1

        sess2 = InferenceSession(
            model_onnx2.SerializeToString(), providers=["CPUExecutionProvider"]
        )
        res2 = sess2.run(None, {"input": (X[:5] * 1e2).astype(np.float64)})
        a2, b2, c2 = res2

        self.assertEqual(b1.max(), b2.max())
        self.assertEqual(b1.min(), b2.min())
        self.assertLess(abs(c1.max() - c2.max()) / c2.max(), 1e-5)
        self.assertLess(abs(c1.min() - c2.min()) / c2.min(), 1e-5)

        self._test_score(
            model, X, TARGET_OPSET, black_op={"ReduceLogSumExp", "ArgMax"}, decimal=2
        )
//  [python]
def __init__(self, threshold=500, train=False):
        if train:
            self.x_train = np.load(self.default_settings['x_train'])
            self.y_train = np.load(self.default_settings['y_train'])
            self.y_train = np.zeros(len(self.x_train))
            self.model = mixture.GaussianMixture(n_components=128, verbose=True, covariance_type='full')
            self.inv_model = None
            self.anomalies = np.load(self.default_settings['anomalies'])
            self.y_test_proba = None
            self.inv_y_test_proba = None
            self.test_valid = np.load(self.default_settings['test_valid'])
            self.test_invalid = np.load(self.default_settings['test_invalid'])
        self.threshold = threshold
        self.inv_threshold = threshold + 100
//  [python]
def __init__(self, threshold=500, train=False):
        if train:
            self.x_train = np.load(self.default_settings['x_train'])
            self.y_train = np.load(self.default_settings['y_train'])
            self.y_train = np.zeros(len(self.x_train))
            self.model = GaussianMixture(n_components=128, verbose=True, covariance_type='full')
            self.inv_model = None
            self.anomalies = np.load(self.default_settings['anomalies'])
            self.y_test_proba = None
            self.inv_y_test_proba = None
            self.test_valid = np.load(self.default_settings['test_valid'])
            self.test_invalid = np.load(self.default_settings['test_invalid'])
        self.threshold = threshold
        self.inv_threshold = threshold + 100
//  [python]
n_clusters = 5
gmm_model = mixture.GaussianMixture(n_components=n_clusters, random_state=5)
//  [python]
n_clusters = 5
gmm_model = GaussianMixture(n_components=n_clusters, random_state=5)
//  [python]
K = 3
    
# 创建色谱
rgb = [[255, 51, 0], 
       [0, 153, 255],
       [138,138,138]]
rgb = np.array(rgb)/255.
cmap_bold = ListedColormap(rgb)

# 采用GMM聚类
gmm = mixture.GaussianMixture(n_components=K, 
                      covariance_type=covariance_type)
//  [python]
K = 3
    
# 创建色谱
rgb = [[255, 51, 0], 
       [0, 153, 255],
       [138,138,138]]
rgb = np.array(rgb)/255.
cmap_bold = ListedColormap(rgb)

# 采用GMM聚类
gmm = GaussianMixture(n_components=K, 
                      covariance_type=covariance_type)
//  [python]
def test_gmmmap_swap():
    from nnmnkwii.baseline.gmm import MLPG
    from sklearn.mixture import GMM as GaussianMixture

    static_dim = 2
    T = 50
    windows = _get_windows_set()[-1]

    np.random.seed(1234)
    src_mc = np.random.rand(T, static_dim * len(windows))
    tgt_mc = np.random.rand(T, static_dim * len(windows)) + 1

    # pseudo parallel data
    XY = np.concatenate((src_mc, tgt_mc), axis=-1)
    gmm = GaussianMixture(n_components=4)
    gmm.fit(XY)

    paramgen = MLPG(gmm, windows=windows, swap=False)
    swap_paramgen = MLPG(gmm, windows=windows, swap=True)

    mc_converted1 = paramgen.transform(src_mc)
    mc_converted2 = swap_paramgen.transform(tgt_mc)

    src_mc = src_mc[:, :static_dim]
    tgt_mc = tgt_mc[:, :static_dim]

    assert norm(tgt_mc - mc_converted1) < norm(src_mc - mc_converted1)
    assert norm(tgt_mc - mc_converted2) > norm(src_mc - mc_converted2)
//  [python]
def test_gmmmap_swap():
    from nnmnkwii.baseline.gmm import MLPG

    static_dim = 2
    T = 50
    windows = _get_windows_set()[-1]

    np.random.seed(1234)
    src_mc = np.random.rand(T, static_dim * len(windows))
    tgt_mc = np.random.rand(T, static_dim * len(windows)) + 1

    # pseudo parallel data
    XY = np.concatenate((src_mc, tgt_mc), axis=-1)
    gmm = GaussianMixture(n_components=4)
    gmm.fit(XY)

    paramgen = MLPG(gmm, windows=windows, swap=False)
    swap_paramgen = MLPG(gmm, windows=windows, swap=True)

    mc_converted1 = paramgen.transform(src_mc)
    mc_converted2 = swap_paramgen.transform(tgt_mc)

    src_mc = src_mc[:, :static_dim]
    tgt_mc = tgt_mc[:, :static_dim]

    assert norm(tgt_mc - mc_converted1) < norm(src_mc - mc_converted1)
    assert norm(tgt_mc - mc_converted2) > norm(src_mc - mc_converted2)
//  [python]
def test_diffvc():
    from nnmnkwii.baseline.gmm import MLPG
    from sklearn.mixture import GMM as GaussianMixture

    # MLPG is performed dimention by dimention, so static_dim 1 is enough, 2 just for in
    # case.
    static_dim = 2
    T = 50

    for windows in _get_windows_set():
        np.random.seed(1234)
        src_mc = np.random.rand(T, static_dim * len(windows))
        tgt_mc = np.random.rand(T, static_dim * len(windows)) + 1

        # pseudo parallel data
        XY = np.concatenate((src_mc, tgt_mc), axis=-1)
        gmm = GaussianMixture(n_components=4)
        gmm.fit(XY)

        paramgen = MLPG(gmm, windows=windows, diff=False)
        diff_paramgen = MLPG(gmm, windows=windows, diff=True)

        mc_converted1 = paramgen.transform(src_mc)
        mc_converted2 = diff_paramgen.transform(src_mc)

        assert mc_converted1.shape == (T, static_dim)
        assert mc_converted2.shape == (T, static_dim)

        src_mc = src_mc[:, :static_dim]
        tgt_mc = tgt_mc[:, :static_dim]
        assert norm(tgt_mc - mc_converted1) < norm(src_mc - mc_converted1)
//  [python]
def test_diffvc():
    from nnmnkwii.baseline.gmm import MLPG

    # MLPG is performed dimention by dimention, so static_dim 1 is enough, 2 just for in
    # case.
    static_dim = 2
    T = 50

    for windows in _get_windows_set():
        np.random.seed(1234)
        src_mc = np.random.rand(T, static_dim * len(windows))
        tgt_mc = np.random.rand(T, static_dim * len(windows)) + 1

        # pseudo parallel data
        XY = np.concatenate((src_mc, tgt_mc), axis=-1)
        gmm = GaussianMixture(n_components=4)
        gmm.fit(XY)

        paramgen = MLPG(gmm, windows=windows, diff=False)
        diff_paramgen = MLPG(gmm, windows=windows, diff=True)

        mc_converted1 = paramgen.transform(src_mc)
        mc_converted2 = diff_paramgen.transform(src_mc)

        assert mc_converted1.shape == (T, static_dim)
        assert mc_converted2.shape == (T, static_dim)

        src_mc = src_mc[:, :static_dim]
        tgt_mc = tgt_mc[:, :static_dim]
        assert norm(tgt_mc - mc_converted1) < norm(src_mc - mc_converted1)
//  [python]
def train_useGMM(data,options):
    model = make_pipeline(StandardScaler(),sklearn.mixture.GMM(n_components=options['n_components'],
                covariance_type=options['covariance_type'],
                max_iter=options['max_iter'],
                n_init=options['n_init']))
    model.fit(data['X_train'],data['y_train'])
    y_pred = model.predict(data['X_valid'])
    acc = metrics.precision_score(data['y_valid'],y_pred,average='micro')
    f1_score = metrics.f1_score(data['y_valid'],y_pred,average='micro')
    recall_score = metrics.recall_score(data['y_valid'],y_pred,average='micro')
    confusion_matrix = metrics.confusion_matrix(data['y_valid'], y_pred)
    res = {'acc':acc,
           'f1_score':f1_score,
           'recall_score':recall_score,
           'confusion_matrix':confusion_matrix}
    return res
//  [python]
def train_useGMM(data,options):
    model = make_pipeline(StandardScaler(),GaussianMixture(n_components=options['n_components'],
                covariance_type=options['covariance_type'],
                max_iter=options['max_iter'],
                n_init=options['n_init']))
    model.fit(data['X_train'],data['y_train'])
    y_pred = model.predict(data['X_valid'])
    acc = metrics.precision_score(data['y_valid'],y_pred,average='micro')
    f1_score = metrics.f1_score(data['y_valid'],y_pred,average='micro')
    recall_score = metrics.recall_score(data['y_valid'],y_pred,average='micro')
    confusion_matrix = metrics.confusion_matrix(data['y_valid'], y_pred)
    res = {'acc':acc,
           'f1_score':f1_score,
           'recall_score':recall_score,
           'confusion_matrix':confusion_matrix}
    return res
//  [python]
def creation_fit(df):
            """
                Define distribution of log(used gas) as Mixure Normal distribution with K components
            """
            K=39
            g = mixture.GaussianMixture(n_components = K)

            data= np.log(df['b']).values.reshape(-1,1)
            gmm= g.fit(data)# fit model

            """
                Define random forest regression between used gas and cpu time
            """
            X = np.transpose([df['b']]) # used gas
            y = np.array(df['d']) # cpu time

            #### configure parameters for the model
            depth= 30
            estimators= 10
            clf = RandomForestRegressor(max_depth=depth, n_estimators=estimators) # RF instance
            clf.fit(X, y) #fit model

            """
                Estimate parameters of log(gas price) as normal distribution
            """
            eps= 0.001 # correction param
            K=35
            gg = mixture.GaussianMixture(n_components = K)
            data= np.log(df['c']+eps).values.reshape(-1,1)
            ggmm= gg.fit(data)# fit model

            return gmm,ggmm,clf
//  [python]
def creation_fit(df):
            """
                Define distribution of log(used gas) as Mixure Normal distribution with K components
            """
            K=39
            g = GaussianMixture(n_components = K)

            data= np.log(df['b']).values.reshape(-1,1)
            gmm= g.fit(data)# fit model

            """
                Define random forest regression between used gas and cpu time
            """
            X = np.transpose([df['b']]) # used gas
            y = np.array(df['d']) # cpu time

            #### configure parameters for the model
            depth= 30
            estimators= 10
            clf = RandomForestRegressor(max_depth=depth, n_estimators=estimators) # RF instance
            clf.fit(X, y) #fit model

            """
                Estimate parameters of log(gas price) as normal distribution
            """
            eps= 0.001 # correction param
            K=35
            gg = GaussianMixture(n_components = K)
            data= np.log(df['c']+eps).values.reshape(-1,1)
            ggmm= gg.fit(data)# fit model

            return gmm,ggmm,clf
//  [python]
def execution_fit(df):
            """
                Define distribution of log(used gas) as Mixure Normal distribution with K components
            """
            K=39
            g = mixture.GaussianMixture(n_components = K)

            data= np.log(df['b']).values.reshape(-1,1)
            gmm= g.fit(data)# fit model

            """
                Define random forest regression between used gas and cpu time
            """
            X = np.transpose([df['b']]) # used gas
            y = np.array(df['d']) # cpu time

            #### configure parameters for the model
            depth= 300
            estimators= 100
            clf = RandomForestRegressor(max_depth=depth, n_estimators=estimators) # RF instance
            clf.fit(X, y) #fit model

            """
                Estimate parameters of log(gas price) as normal distribution
            """
            eps= 0.001 # correction param
            K=65
            gg = mixture.GaussianMixture(n_components = K)
            data= np.log(df['c']+eps).values.reshape(-1,1)
            ggmm= gg.fit(data)# fit model


            return gmm,ggmm,clf
//  [python]
def execution_fit(df):
            """
                Define distribution of log(used gas) as Mixure Normal distribution with K components
            """
            K=39
            g = GaussianMixture(n_components = K)

            data= np.log(df['b']).values.reshape(-1,1)
            gmm= g.fit(data)# fit model

            """
                Define random forest regression between used gas and cpu time
            """
            X = np.transpose([df['b']]) # used gas
            y = np.array(df['d']) # cpu time

            #### configure parameters for the model
            depth= 300
            estimators= 100
            clf = RandomForestRegressor(max_depth=depth, n_estimators=estimators) # RF instance
            clf.fit(X, y) #fit model

            """
                Estimate parameters of log(gas price) as normal distribution
            """
            eps= 0.001 # correction param
            K=65
            gg = GaussianMixture(n_components = K)
            data= np.log(df['c']+eps).values.reshape(-1,1)
            ggmm= gg.fit(data)# fit model


            return gmm,ggmm,clf
//  [python]
def test_uniradius_obstacle_from_gmm(visualize=False):
    """Test to verify the consistent behavior between the Gmm-obstacle and the
    corresponding analytic one."""
    dimension = 2
    n_gmms = 1
    gmm_ellipse = GmmObstacle(n_gmms=1)
    gmm_ellipse._gmm = mixture.GaussianMixture(n_components=n_gmms)
    gmm_ellipse._gmm.means_ = np.ones((n_gmms, dimension))
    gmm_ellipse._gmm.covariances_ = 1.0 * np.eye(dimension).reshape(
        n_gmms, dimension, dimension
    )
    gmm_ellipse._gmm.precisions_cholesky_ = LA.pinv(gmm_ellipse._gmm.covariances_)
    gmm_ellipse._gmm.weights = np.ones(n_gmms)

    simple_ellipse = gmm_ellipse.transform_to_analytic_ellipses()

    # Test different positions
    position = np.array([4, 1])
    gamma_analytic = simple_ellipse[-1].get_gamma(position, in_obstacle_frame=False)
    gamma_vals_gmm_prop = gmm_ellipse.get_gamma_proportional(position, index=0)
    gamma_vals_gmm = gmm_ellipse.get_gamma(position, index=0)

    assert np.isclose(gamma_analytic, gamma_vals_gmm), "Gamma values are not close"
    # assert np.isclose(gamma_analytic, gamma_vals_gmm_prop), "Gamma values are not close"

    if visualize:
        plot_gammas_multigamma(gmm_ellipse, simple_ellipse)
//  [python]
def test_uniradius_obstacle_from_gmm(visualize=False):
    """Test to verify the consistent behavior between the Gmm-obstacle and the
    corresponding analytic one."""
    dimension = 2
    n_gmms = 1
    gmm_ellipse = GmmObstacle(n_gmms=1)
    gmm_ellipse._gmm = GaussianMixture(n_components=n_gmms)
    gmm_ellipse._gmm.means_ = np.ones((n_gmms, dimension))
    gmm_ellipse._gmm.covariances_ = 1.0 * np.eye(dimension).reshape(
        n_gmms, dimension, dimension
    )
    gmm_ellipse._gmm.precisions_cholesky_ = LA.pinv(gmm_ellipse._gmm.covariances_)
    gmm_ellipse._gmm.weights = np.ones(n_gmms)

    simple_ellipse = gmm_ellipse.transform_to_analytic_ellipses()

    # Test different positions
    position = np.array([4, 1])
    gamma_analytic = simple_ellipse[-1].get_gamma(position, in_obstacle_frame=False)
    gamma_vals_gmm_prop = gmm_ellipse.get_gamma_proportional(position, index=0)
    gamma_vals_gmm = gmm_ellipse.get_gamma(position, index=0)

    assert np.isclose(gamma_analytic, gamma_vals_gmm), "Gamma values are not close"
    # assert np.isclose(gamma_analytic, gamma_vals_gmm_prop), "Gamma values are not close"

    if visualize:
        plot_gammas_multigamma(gmm_ellipse, simple_ellipse)
//  [python]
def test_obstacle_with_radius_3_from_gmm(visualize=False):
    """Test to verify the consistent behavior between the Gmm-obstacle and the
    corresponding analytic one."""
    dimension = 2
    n_gmms = 1

    gmm_ellipse = GmmObstacle(n_gmms=1)
    gmm_ellipse._gmm = mixture.GaussianMixture(n_components=n_gmms)
    gmm_ellipse._gmm.means_ = np.zeros((n_gmms, dimension))
    gmm_ellipse._gmm.covariances_ = 3.0 * np.eye(dimension).reshape(
        n_gmms, dimension, dimension
    )
    gmm_ellipse._gmm.precisions_cholesky_ = LA.pinv(gmm_ellipse._gmm.covariances_)
    gmm_ellipse._gmm.weights = np.ones(n_gmms)

    simple_ellipse = gmm_ellipse.transform_to_analytic_ellipses()

    # Test different positions
    position = np.array([4, 1])
    gamma_analytic = simple_ellipse[-1].get_gamma(position, in_obstacle_frame=False)
    gamma_vals_gmm = gmm_ellipse.get_gamma(position, index=0)

    assert np.isclose(gamma_analytic, gamma_vals_gmm), "Gamma values are not close"

    if visualize:
        plot_gammas_multigamma(gmm_ellipse, simple_ellipse)
//  [python]
def test_obstacle_with_radius_3_from_gmm(visualize=False):
    """Test to verify the consistent behavior between the Gmm-obstacle and the
    corresponding analytic one."""
    dimension = 2
    n_gmms = 1

    gmm_ellipse = GmmObstacle(n_gmms=1)
    gmm_ellipse._gmm = GaussianMixture(n_components=n_gmms)
    gmm_ellipse._gmm.means_ = np.zeros((n_gmms, dimension))
    gmm_ellipse._gmm.covariances_ = 3.0 * np.eye(dimension).reshape(
        n_gmms, dimension, dimension
    )
    gmm_ellipse._gmm.precisions_cholesky_ = LA.pinv(gmm_ellipse._gmm.covariances_)
    gmm_ellipse._gmm.weights = np.ones(n_gmms)

    simple_ellipse = gmm_ellipse.transform_to_analytic_ellipses()

    # Test different positions
    position = np.array([4, 1])
    gamma_analytic = simple_ellipse[-1].get_gamma(position, in_obstacle_frame=False)
    gamma_vals_gmm = gmm_ellipse.get_gamma(position, index=0)

    assert np.isclose(gamma_analytic, gamma_vals_gmm), "Gamma values are not close"

    if visualize:
        plot_gammas_multigamma(gmm_ellipse, simple_ellipse)
//  [python]
def gmm_gate (adata, marker_of_interest):
    """
Parameters:

    adata : Ann Data Object
    marker_of_interest : string
        Marker of interest.

Returns:
    Distribution plot of the marker of interest along with GMM overlaid.
    
Example:

    sm.pl.gmm_dist_plot (adata, marker_of_interest='CD45')

    """
    
    # If no raw data is available make a copy
    if adata.raw is None:
        adata.raw = adata
    
    # Copy of the raw data if it exisits
    if adata.raw is not None:
        adata.X = adata.raw.X
    
    # Make a copy of the data with the marker of interest
    data = pd.DataFrame(np.log1p(adata.X), columns = adata.var.index, index= adata.obs.index)[[marker_of_interest]]
    
    # Clip of the top and bottom outliers before applying the model
    data = data.clip(lower =np.percentile(data,1), upper=np.percentile(data,99))
    
    # Apply Gaussian mixture model
    m = data[marker_of_interest].values
    data_gm = m.reshape(-1, 1)
    gmm = mixture.GaussianMixture(n_components=2)
    gmm.fit(data_gm)
    gate = np.mean(gmm.means_)
    
    mean = gmm.means_
    cov = gmm.covariances_
    std = [ np.sqrt(  np.trace(cov[i])/2) for i in range(0,2) ]
    #std = np.sqrt(np.trace(cov)/2) 
    
    
    # Merge with image ID
    if len(adata.obs['ImageId'].unique()) > 1:
        # Identify the most and least positivity of the given marker of interest
        # Generate a dataframe with various gates
        dd = data.values
        dd = np.where(dd < gate, np.nan, dd)
        np.warnings.filterwarnings('ignore')
        dd = np.where(dd > gate, 1, dd)
        dd = pd.DataFrame(dd, index = data.index, columns = ['gate'])
        dd = dd.merge(pd.DataFrame(adata.obs['ImageId']), how='outer', left_index=True, right_index=True)
        dd = dd[dd['gate'] == 1]
        image_positivity = dd.groupby('ImageId').count().sort_values('gate')
        # Create a string for overlaying on image
        low = list(image_positivity.head(3).index)
        low = ','.join(low)
        high = list(image_positivity.tail(3).index)
        high = ','.join(high)
        # Dor image
        textstr = '\n'.join((
            ("Most positive in: ", 
             high,
             "Least positive in ",
             low
            )))

    # Plot
    sns.set_style("white")
    fig, ax = plt.subplots()
    x = np.linspace(mean[0] - 3*std[0], mean[0] + 3*std[0], 1000)
    y = np.linspace(mean[1] - 3*std[1], mean[1] + 3*std[1], 1000)
    sns.distplot(data[marker_of_interest], color = 'grey')
    plt.axvline(x= mean[0], c='#000000', linestyle='dotted')
    plt.axvline(x= mean[1], c='#000000', linestyle='dotted')
    plt.plot(x, stats.norm.pdf(x, mean[0], std[0]), linestyle='dashed')
    plt.plot(y, stats.norm.pdf(y, mean[1], std[1]), linestyle='dashed')
    plt.xticks(np.arange(min(data[marker_of_interest])-1, max(data[marker_of_interest])+1, 0.5))
    plt.title(marker_of_interest, fontsize=20)    
    if len(adata.obs['ImageId'].unique()) > 1:
        ax.text(0.05, 0.95, textstr, fontsize=14, transform=ax.transAxes,
            verticalalignment='top')
//  [python]
def gmm_gate (adata, marker_of_interest):
    """
Parameters:

    adata : Ann Data Object
    marker_of_interest : string
        Marker of interest.

Returns:
    Distribution plot of the marker of interest along with GMM overlaid.
    
Example:
```python
    sm.pl.gmm_dist_plot (adata, marker_of_interest='CD45')
```
    """
    
    # If no raw data is available make a copy
    if adata.raw is None:
        adata.raw = adata
    
    # Copy of the raw data if it exisits
    if adata.raw is not None:
        adata.X = adata.raw.X
    
    # Make a copy of the data with the marker of interest
    data = pd.DataFrame(np.log1p(adata.X), columns = adata.var.index, index= adata.obs.index)[[marker_of_interest]]
    
    # Clip of the top and bottom outliers before applying the model
    data = data.clip(lower =np.percentile(data,1), upper=np.percentile(data,99))
    
    # Apply Gaussian mixture model
    m = data[marker_of_interest].values
    data_gm = m.reshape(-1, 1)
    gmm = GaussianMixture(n_components=2)
    gmm.fit(data_gm)
    gate = np.mean(gmm.means_)
    
    mean = gmm.means_
    cov = gmm.covariances_
    std = [ np.sqrt(  np.trace(cov[i])/2) for i in range(0,2) ]
    #std = np.sqrt(np.trace(cov)/2) 
    
    
    # Merge with image ID
    if len(adata.obs['ImageId'].unique()) > 1:
        # Identify the most and least positivity of the given marker of interest
        # Generate a dataframe with various gates
        dd = data.values
        dd = np.where(dd < gate, np.nan, dd)
        np.warnings.filterwarnings('ignore')
        dd = np.where(dd > gate, 1, dd)
        dd = pd.DataFrame(dd, index = data.index, columns = ['gate'])
        dd = dd.merge(pd.DataFrame(adata.obs['ImageId']), how='outer', left_index=True, right_index=True)
        dd = dd[dd['gate'] == 1]
        image_positivity = dd.groupby('ImageId').count().sort_values('gate')
        # Create a string for overlaying on image
        low = list(image_positivity.head(3).index)
        low = ','.join(low)
        high = list(image_positivity.tail(3).index)
        high = ','.join(high)
        # Dor image
        textstr = '\n'.join((
            ("Most positive in: ", 
             high,
             "Least positive in ",
             low
            )))

    # Plot
    sns.set_style("white")
    fig, ax = plt.subplots()
    x = np.linspace(mean[0] - 3*std[0], mean[0] + 3*std[0], 1000)
    y = np.linspace(mean[1] - 3*std[1], mean[1] + 3*std[1], 1000)
    sns.distplot(data[marker_of_interest], color = 'grey')
    plt.axvline(x= mean[0], c='#000000', linestyle='dotted')
    plt.axvline(x= mean[1], c='#000000', linestyle='dotted')
    plt.plot(x, stats.norm.pdf(x, mean[0], std[0]), linestyle='dashed')
    plt.plot(y, stats.norm.pdf(y, mean[1], std[1]), linestyle='dashed')
    plt.xticks(np.arange(min(data[marker_of_interest])-1, max(data[marker_of_interest])+1, 0.5))
    plt.title(marker_of_interest, fontsize=20)    
    if len(adata.obs['ImageId'].unique()) > 1:
        ax.text(0.05, 0.95, textstr, fontsize=14, transform=ax.transAxes,
            verticalalignment='top')
//  [python]
def _select_gmm(cls, real_data, n_components, covariance_type):
        if isinstance(n_components, int):
            min_comp = max_comp = n_components
        else:
            min_comp, max_comp = n_components

        if isinstance(covariance_type, str):
            covariance_type = (covariance_type, )

        combinations = list(itertools.product(range(min_comp, max_comp + 1), covariance_type))
        if len(combinations) == 1:
            return combinations[0]

        lowest_bic = np.inf
        best = None
        for n_components, covariance_type in combinations:
            gmm = mixture.GaussianMixture(n_components=n_components, covariance_type=covariance_type)
            try:
                gmm.fit(real_data)
                bic = gmm.bic(real_data)
                LOGGER.debug('%s, %s: %s', n_components, covariance_type, bic)
                if bic < lowest_bic:
                    lowest_bic = bic
                    best = (n_components, covariance_type)

            except ValueError:
                pass

        if not best:
            metric_name = cls.name
            raise IncomputableMetricError(f'{metric_name}: Unable to fit GaussianMixture')

        return best
//  [python]
def _select_gmm(cls, real_data, n_components, covariance_type):
        if isinstance(n_components, int):
            min_comp = max_comp = n_components
        else:
            min_comp, max_comp = n_components

        if isinstance(covariance_type, str):
            covariance_type = (covariance_type, )

        combinations = list(itertools.product(range(min_comp, max_comp + 1), covariance_type))
        if len(combinations) == 1:
            return combinations[0]

        lowest_bic = np.inf
        best = None
        for n_components, covariance_type in combinations:
            gmm = GaussianMixture(n_components=n_components, covariance_type=covariance_type)
            try:
                gmm.fit(real_data)
                bic = gmm.bic(real_data)
                LOGGER.debug('%s, %s: %s', n_components, covariance_type, bic)
                if bic < lowest_bic:
                    lowest_bic = bic
                    best = (n_components, covariance_type)

            except ValueError:
                pass

        if not best:
            metric_name = cls.name
            raise IncomputableMetricError(f'{metric_name}: Unable to fit GaussianMixture')

        return best
//  [python]
def test_project_point_on_surface(visualize=False):
    n_gmms = 1
    dimension = 2

    gmm_ellipse = GmmObstacle(n_gmms=n_gmms)
    gmm_ellipse._gmm = mixture.GaussianMixture(n_components=gmm_ellipse.n_gmms)
    gmm_ellipse._gmm.means_ = np.zeros((gmm_ellipse.n_gmms, dimension))
    gmm_ellipse._gmm.means_[0, :] = [0.0, 0]

    gmm_ellipse._gmm.covariances_ = np.zeros((n_gmms, dimension, dimension))
    gmm_ellipse._gmm.covariances_[0, :, :] = [[1.0, 0], [0, 4.0]]

    gmm_ellipse._gmm.precisions_cholesky_ = np.zeros((n_gmms, dimension, dimension))
    for ii in range(n_gmms):
        gmm_ellipse._gmm.precisions_cholesky_[ii, :, :] = LA.pinv(
            gmm_ellipse._gmm.covariances_[ii, :, :]
        )
    gmm_ellipse._gmm.weights_ = np.ones(n_gmms) / n_gmms
    # gmm_ellipse.evaluate_hirarchy_and_reference_points()

    position = np.array([0, 6])
    proj_pos = gmm_ellipse.project_point_on_surface(position, index=0)
    assert np.allclose(proj_pos, [0, 4])

    position = np.array([3, 5.6])
    proj_pos = gmm_ellipse.project_point_on_surface(position, index=0)
    assert np.isclose(gmm_ellipse.get_gamma(proj_pos, index=0), 1)

    if visualize:
        n_resolution = 10
        x_lim = [-8, 8]
        y_lim = [-8, 8]

        nx = ny = n_resolution
        x_vals, y_vals = np.meshgrid(
            np.linspace(x_lim[0], x_lim[1], nx),
            np.linspace(y_lim[0], y_lim[1], ny),
        )

        fig, ax = plt.subplots(1, 1, figsize=(7, 6))
        gmm_ellipse.plot_obstacle(ax=ax, alpha_obstacle=0.2)

        # levels = np.linspace(0.01, 1., 20 + 1)
        positions = np.vstack((x_vals.reshape(1, -1), y_vals.reshape(1, -1)))
        proj_pos = np.zeros(positions.shape)

        for ii in range(positions.shape[1]):
            proj_pos[:, ii] = gmm_ellipse.project_point_on_surface(
                positions[:, ii], index=0
            )

            ax.plot(
                [positions[0, ii], proj_pos[0, ii]],
                [positions[1, ii], proj_pos[1, ii]],
                "k--",
            )

        ax.plot(positions[0, :], positions[1, :], "ro")
        ax.plot(proj_pos[0, :], proj_pos[1, :], "go")

        ax.set_aspect("equal", adjustable="box")
        ax.set_xlim(x_lim)
        ax.set_ylim(x_lim)
        ax.grid()
//  [python]
def test_project_point_on_surface(visualize=False):
    n_gmms = 1
    dimension = 2

    gmm_ellipse = GmmObstacle(n_gmms=n_gmms)
    gmm_ellipse._gmm = GaussianMixture(n_components=gmm_ellipse.n_gmms)
    gmm_ellipse._gmm.means_ = np.zeros((gmm_ellipse.n_gmms, dimension))
    gmm_ellipse._gmm.means_[0, :] = [0.0, 0]

    gmm_ellipse._gmm.covariances_ = np.zeros((n_gmms, dimension, dimension))
    gmm_ellipse._gmm.covariances_[0, :, :] = [[1.0, 0], [0, 4.0]]

    gmm_ellipse._gmm.precisions_cholesky_ = np.zeros((n_gmms, dimension, dimension))
    for ii in range(n_gmms):
        gmm_ellipse._gmm.precisions_cholesky_[ii, :, :] = LA.pinv(
            gmm_ellipse._gmm.covariances_[ii, :, :]
        )
    gmm_ellipse._gmm.weights_ = np.ones(n_gmms) / n_gmms
    # gmm_ellipse.evaluate_hirarchy_and_reference_points()

    position = np.array([0, 6])
    proj_pos = gmm_ellipse.project_point_on_surface(position, index=0)
    assert np.allclose(proj_pos, [0, 4])

    position = np.array([3, 5.6])
    proj_pos = gmm_ellipse.project_point_on_surface(position, index=0)
    assert np.isclose(gmm_ellipse.get_gamma(proj_pos, index=0), 1)

    if visualize:
        n_resolution = 10
        x_lim = [-8, 8]
        y_lim = [-8, 8]

        nx = ny = n_resolution
        x_vals, y_vals = np.meshgrid(
            np.linspace(x_lim[0], x_lim[1], nx),
            np.linspace(y_lim[0], y_lim[1], ny),
        )

        fig, ax = plt.subplots(1, 1, figsize=(7, 6))
        gmm_ellipse.plot_obstacle(ax=ax, alpha_obstacle=0.2)

        # levels = np.linspace(0.01, 1., 20 + 1)
        positions = np.vstack((x_vals.reshape(1, -1), y_vals.reshape(1, -1)))
        proj_pos = np.zeros(positions.shape)

        for ii in range(positions.shape[1]):
            proj_pos[:, ii] = gmm_ellipse.project_point_on_surface(
                positions[:, ii], index=0
            )

            ax.plot(
                [positions[0, ii], proj_pos[0, ii]],
                [positions[1, ii], proj_pos[1, ii]],
                "k--",
            )

        ax.plot(positions[0, :], positions[1, :], "ro")
        ax.plot(proj_pos[0, :], proj_pos[1, :], "go")

        ax.set_aspect("equal", adjustable="box")
        ax.set_xlim(x_lim)
        ax.set_ylim(x_lim)
        ax.grid()