A fast, pure python implementation of the MuyGPs Gaussian process realization and training algorithm.

Overview

pipeline status Documentation Status

Fast implementation of the MuyGPs Gaussian process hyperparameter estimation algorithm

MuyGPs is a GP estimation method that affords fast hyperparameter optimization by way of performing leave-one-out cross-validation. MuyGPs achieves best-in-class speed and scalability by limiting inference to the information contained in k nearest neighborhoods for prediction locations for both hyperparameter optimization and tuning. This feature affords the optimization of hyperparameters by way of leave-one-out cross-validation, as opposed to the more expensive loglikelihood evaluations requires by similar sparse methods.

Installation

Pip installation instructions:

$ pip install muygpys

To install from source, follow these instructions:

$ git clone [email protected]:LLNL/MuyGPyS.git
$ pip install -e MuyGPyS

Building Docs

Automatically-generated documentation can be found at readthedocs.io.

Doc building instructions:

$ cd /path/to/this/repo/docs
$ pip install -r requirements.txt
$ sphinx-build -b html docs docs/_build/html

Then open the file docs/_build/html/index.html in your browser of choice.

The Basics

Data format

MuyGPyS expects that each train or test observation corresponds to a row index in feature and response matrices. In our examples we assume that train data is bundled into a (train_count, feature_count) feature matrix train_features and a (train_count, response_count) response matrix train_responses. In classification examples we will instead refer to a (train_count, class_count) label matrix train_labels whose rows are one-hot encodings. Our examples will assume that the data is accessible via imaginary getter functions.

Constructing Nearest Neighbor Lookups

MuyGPyS.neighbors.NN_Wrapper is an api for tasking several KNN libraries with the construction of lookup indexes that empower fast training and inference. The wrapper constructor expects the training features, the number of nearest neighbors, and a method string specifying which algorithm to use, as well as any additional kwargs used by the methods. Currently supported implementations include exact KNN using sklearn ("exact") and approximate KNN using hnsw ("hnsw").

Construct exact and approximate KNN data example with k = 10

>> approx_nbrs_lookup = NN_Wrapper(train_features, nn_count, nn_method="hnsw", space="l2", M=16) ">
>>> from MuyGPyS.neighors import NN_Wrapper 
>>> train_features = load_train_features()  # imaginary getter
>>> nn_count = 10
>>> exact_nbrs_lookup = NN_Wrapper(train_features, nn_count, nn_method="exact", algorithm="ball_tree")
>>> approx_nbrs_lookup = NN_Wrapper(train_features, nn_count, nn_method="hnsw", space="l2", M=16)

These lookup data structures are then usable to find nearest neighbors of queries in the training data.

Sampling Batches of Data

MuyGPyS includes convenience functions for sampling batches of data from existing datasets. These batches are returned in the form of row indices, both of the sampled data as well as their nearest neighbors. Also included is the ability to sample "balanced" batches, where the data is partitioned by class and we attempt to sample as close to an equal number of items from each class as is possible.

Sampling random and balanced (for classification) batches of 100 elements:

>> balanced_indices, balanced_nn_indices = get_balanced_batch( ... exact_nbrs_lookup, train_lookup, batch_count ... ) # Classification only! ">
>>> from MuyGPyS.optimize.batch import sample_batch, get_balanced_batch
>>> train_labels = load_train_labels()  # imaginary getter
>>> batch_count = 200
>>> train_count, _ = train_features.shape
>>> batch_indices, batch_nn_indices = sample_batch(
...         exact_nbrs_lookup, batch_count, train_count
... )
>>> train_lookup = np.argmax(train["output"], axis=1)
>>> balanced_indices, balanced_nn_indices = get_balanced_batch(
...         exact_nbrs_lookup, train_lookup, batch_count
... ) # Classification only!

These indices and nn_indices arrays are the basic operating blocks of MuyGPyS linear algebraic inference. The elements of indices.shape == (batch_count,) lists all of the row indices into train's matrices corresponding to the sampled data. The rows of nn_indices.shape == (batch_count, nn_count) list the row indices into train's matrices corresponding to the nearest neighbors of the sampled data. While the user need not use MuyGPyS sampling tools to construct these data, they will need to construct similar indices into their data in order to use MuyGPyS.

Setting and Optimizing Hyperparameters

One initializes a MuyGPS object by indicating the kernel, as well as optionally specifying hyperparameters.

Creating a Matern kernel:

>> muygps = MuyGPS(**k_kwarg) ">
>>> from MuyGPyS.gp.muygps import MuyGPS
>>> k_kwargs = {
...         "kern": "rbf",
...         "metric": "F2",
...         "eps": {"val": 1e-5},
...         "nu": {"val": 0.38, "bounds": (0.1, 2.5)},
...         "length_scale": {"val": 7.2},
... }
>>> muygps = MuyGPS(**k_kwarg)

Hyperparameters can be initialized or reset using dictionary arguments containing the optional "val" and "bounds" keys. "val" sets the hyperparameter to the given value, and "bounds" determines the upper and lower bounds to be used for optimization. If "bounds" is set, "val" can also take the arguments "sample" and "log_sample" to generate a uniform or log uniform sample, respectively. If "bounds" is set to "fixed", the hyperparameter will remain fixed during any optimization. This is the default behavior for all hyperparameters if "bounds" is unset by the user.

One sets hyperparameters such as eps, sigma_sq, as well as kernel-specific hyperparameters, e.g. nu and length_scale for the Matern kernel, at initialization as above. All hyperparameters other than sigma_sq are assumed to be fixed unless otherwise specified.

MuyGPyS depends upon linear operations on specially-constructed tensors in order to efficiently estimate GP realizations. Constructing these tensors depends upon the nearest neighbor index matrices that we described above. We can construct a distance tensor coalescing all of the square pairwise distance matrices of the nearest neighbors of a batch of points. This snippet constructs a Euclidean distance tensor.

>>> from MuyGPyS.gp.distance import pairwise_distances
>>> pairwise_dists = pairwise_distances(
...         train_features, batch_nn_indices, metric="l2"
... )

We can similarly construct a matrix coalescing all of the distance vectors between the same batch of points and their nearest neighbors.

>>> from MuyGPyS.gp.distance import crosswise_distances
>>> crosswise_dists = crosswise_distances(
...         train_features,
...         train_features,
...         batch_indices,
...         batch_nn_indices,
...         metric='l2',
... )

We can easily realize kernel tensors using a MuyGPS object's kernel functor:

>>> K = muygps.kernel(pairwise_dists)
>>> Kcross = muygps.kernel(crosswise_dists)

We supply a convenient leave-one-out cross-validation utility that internally realizes kernel tensors in this manner.

jac: array([-3.06976666e-06]) message: b'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL' nfev: 16 nit: 5 njev: 8 status: 0 success: True x: array([0.39963594]) ">
>>> from MuyGPyS.optimize.chassis.scipy_optimize_from_tensors
>>> scipy_optimize_from_tensors(
...         muygps,
...         batch_indices,
...         batch_nn_indices,
...         crosswise_dists,
...         pairwise_dists,
...         train_labels,
...         loss_method="mse",
...         verbose=True,
... )
parameters to be optimized: ['nu']
bounds: [[0.1 1. ]]
sampled x0: [0.8858425]
optimizer results:
      fun: 0.4797763813693626
 hess_inv: <1x1 LbfgsInvHessProduct with dtype=float64>
      jac: array([-3.06976666e-06])
  message: b'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL'
     nfev: 16
      nit: 5
     njev: 8
   status: 0
  success: True
        x: array([0.39963594])

If you do not need to keep the distance tensors around for reference, you can use a related function:

>>> from MuyGPyS.optimize.chassis.scipy_optimize_from_indices
>>> scipy_optimize_from_indices(
...         muygps,
...         batch_indices,
...         batch_nn_indices,
...         train_features,
...	    test_features,
...	    train_labels,
...         loss_method="mse",
...         verbose=False,
... )

One-line model creation

The library also provides one-line APIs for creating MuyGPs models intended for regression and classification. The functions are MuyGPyS.examples.regress.make_regressor and MuyGPyS.examples.classify.make_classifier, respectively. These functions provide convenient mechanisms for specifying and optimizing models if you have no need to later reference their intermediate data structures (such as the training batches or their distance tensors). They return only the trained MuyGPyS.gp.muygps.MuyGPS model and the MuyGPyS.neighbors.NN_Wrapper neighbors lookup data structure.

An example regressor. In order to automatically train sigma_sq, set k_kwargs["sigma_sq"] = "learn". Note that this is the default behavior, and sigma_sq is the only hyperparameter assumed to be a training target by default.

>> k_kwargs = { ... "kern": "rbf", ... "metric": "F2", ... "eps": {"val": 1e-5}, ... "nu": {"val": 0.38, "bounds": (0.1, 2.5)}, ... "length_scale": {"val": 7.2}, ... "sigma_sq": "learn", ... } >>> muygps, nbrs_lookup = make_regressor( ... train_features, ... train_responses, ... nn_count=40, ... batch_size=500, ... loss_method="mse", ... k_kwargs=k_kwargs, ... nn_kwargs=nn_kwargs, ... verbose=False, ... ) ">
>>> from MuyGPyS.examples.regress import make_regressor
>>> train_features, train_responses = load_train()  # imaginary train getter
>>> test_features, test_responses = load_test()  # imaginary test getter
>>> nn_kwargs = {"nn_method": "exact", "algorithm": "ball_tree"}
>>> k_kwargs = {
...         "kern": "rbf",
...         "metric": "F2",
...         "eps": {"val": 1e-5},
...         "nu": {"val": 0.38, "bounds": (0.1, 2.5)},
...         "length_scale": {"val": 7.2},
...         "sigma_sq": "learn",
... }
>>> muygps, nbrs_lookup = make_regressor(
...         train_features,
...         train_responses,
...         nn_count=40,
...         batch_size=500,
...         loss_method="mse",
...         k_kwargs=k_kwargs,
...         nn_kwargs=nn_kwargs,
...         verbose=False,
... )    

An example surrogate classifier.

>> k_kwargs = { ... "kern": "rbf", ... "metric": "F2", ... "eps": {"val": 1e-5}, ... "nu": {"val": 0.38, "bounds": (0.1, 2.5)}, ... "length_scale": {"val": 7.2}, ... } >>> muygps, nbrs_lookup = make_classifier( ... train_features, ... train_labels, ... nn_count=40, ... batch_size=500, ... loss_method="log", ... k_kwargs=k_kwargs, ... nn_kwargs=nn_kwargs, ... verbose=False, ... ) ">
>>> from MuyGPyS.examples.classify import make_classifier
>>> train_features, train_labels = load_train()  # imaginary train getter
>>> nn_kwargs = {"nn_method": "exact", "algorithm": "ball_tree"}
>>> k_kwargs = {
...         "kern": "rbf",
...         "metric": "F2",
...         "eps": {"val": 1e-5},
...         "nu": {"val": 0.38, "bounds": (0.1, 2.5)},
...         "length_scale": {"val": 7.2},
... }
>>> muygps, nbrs_lookup = make_classifier(
...         train_features,
...         train_labels,
...         nn_count=40,
...         batch_size=500,
...         loss_method="log",
...         k_kwargs=k_kwargs,
...         nn_kwargs=nn_kwargs, 
...         verbose=False,
... )    

Multivariate Models

MuyGPyS also supports multivariate models via the MuyGPyS.gp.muygps.MultivariateMuyGPS class, which maintains a separte kernel function for each response dimension. This class is similar in interface to MuyGPyS.gp.muygps.MuyGPS, but requires a list of hyperparameter dicts at initialization. See the following example:

>> mmuygps = MMuyGPS("matern", **k_args) ">
>>> from MuyGPyS.gp.muygps import MultivariateMuyGPS as MMuyGPS
>>> k_args = [
... 	    {
...                 "eps": {"val": 1e-5},
...                 "nu": {"val": 0.38, "bounds": (0.1, 2.5)},
...                 "length_scale": {"val": 7.2},
...	    },
... 	    {
...                 "eps": {"val": 1e-5},
...                 "nu": {"val": 0.67, "bounds": (0.1, 2.5)},
...                 "length_scale": {"val": 7.2},
...	    },
... ]
>>> mmuygps = MMuyGPS("matern", **k_args)

Training is similar, and depends upon the same neighbors index datastructures as the singular models. In order to train, one need only loop over the models contained within the multivariate object.

>>> from MuyGPyS.optimize.chassis.scipy_optimize_from_indices
>>> for i, model in mmuygps.models:
>>>         scipy_optimize_from_indices(
...                 model,
...                 batch_indices,
...                 batch_nn_indices,
...                 train_features,
...	            test_features,
...	            train_responses[:, i].reshape(train_count, 1),
...                 loss_method="mse",
...                 verbose=False,
...         )

We also support one-line make functions for regression and classification:

>> k_args = [ ... { ... "eps": {"val": 1e-5}, ... "nu": {"val": 0.38, "bounds": (0.1, 2.5)}, ... "length_scale": {"val": 7.2}, ... }, ... { ... "eps": {"val": 1e-5}, ... "nu": {"val": 0.67, "bounds": (0.1, 2.5)}, ... "length_scale": {"val": 7.2}, ... }, ... ] >>> muygps, nbrs_lookup = make_multivariate_regressor( ... train_features, ... train_responses, ... nn_count=40, ... batch_size=500, ... loss_method="mse", ... kern="matern", ... k_args=k_args, ... nn_kwargs=nn_kwargs, ... verbose=False, ... ) ">
>>> from MuyGPyS.examples.regress import make_multivariate_regressor
>>> train_features, train_responses = load_train()  # imaginary train getter
>>> nn_kwargs = {"nn_method": "exact", "algorithm": "ball_tree"}
>>> k_args = [
... 	    {
...                 "eps": {"val": 1e-5},
...                 "nu": {"val": 0.38, "bounds": (0.1, 2.5)},
...                 "length_scale": {"val": 7.2},
...	    },
... 	    {
...                 "eps": {"val": 1e-5},
...                 "nu": {"val": 0.67, "bounds": (0.1, 2.5)},
...                 "length_scale": {"val": 7.2},
...	    },
... ]
>>> muygps, nbrs_lookup = make_multivariate_regressor(
...         train_features,
...         train_responses,
...         nn_count=40,
...         batch_size=500,
...         loss_method="mse",
...	    kern="matern",
...         k_args=k_args,
...         nn_kwargs=nn_kwargs,
...         verbose=False,
... )    

Inference

With set hyperparameters, we are able to use the muygps object to predict the response of test data. Several workflows are supported. See below a simple regression workflow, using the data structures built up in this example:

>> pairwise_dists = pairwise_distances( ... train_features, batch_nn_indices, metric="l2" ... ) >>> crosswise_dists = crosswise_distances( ... test_features, ... train_features, ... indices, ... nn_indices, ... metric='l2', ... ) >>> K = muygps.kernel(pairwise_dists) >>> Kcross = muygps.kernel(crosswise_dists) >>> predictions = muygps.regress(K, Kcross, train_responses[nn_indices, :]) ">
>>> indices = np.arange(test_count)
>>> nn_indices = train_nbrs_lookup.get_nns(test["input"])
>>> pairwise_dists = pairwise_distances(
...         train_features, batch_nn_indices, metric="l2"
... )
>>> crosswise_dists = crosswise_distances(
...         test_features,
...         train_features,
...         indices,
...         nn_indices,
...         metric='l2',
... )
>>> K = muygps.kernel(pairwise_dists)
>>> Kcross = muygps.kernel(crosswise_dists)
>>> predictions = muygps.regress(K, Kcross, train_responses[nn_indices, :])

Again if you do not want to reuse your tensors, you can run the more compact:

>> muygps.regress_from_indices( ... indices, ... nn_indices, ... test_features, ... train_features, ... train_responses, ... ) ">
>>> indices = np.arange(test_count)
>>> nn_indices = train_nbrs_lookup.get_nns(test["input"])
>>> muygps.regress_from_indices(
...         indices,
...	    nn_indices,
...	    test_features,
...	    train_features,
...	    train_responses,
... )

Multivariate models support the same functions.

More complex workflows are of course available. See the MuyGPyS.examples high-level API functions for examples.

API Examples

Listed below are several examples using the high-level APIs located in MuyGPyS.examples.classify and MuyGPyS.examples.regress. Note that one need not go through these APIs to use MuyGPyS, but they condense many basic workflows into a single function call. In all of these examples, note that if all of the hyperparameters are fixed in k_kwargs (i.e. you supply no optimization bounds), the API will perform no optimization and will instead simply predict on the data problem using the provided kernel. While these examples all use a single model, one can modify those with multivariate responses to use multivariate models by supplying the additional keyword argument kern=kernel_name, for kernel_name in ['rbf', 'matern'] and providing a list of hyperparameter dicts to the keyword argument k_kwargs as above.

Regression

The following example performs GP regression on the Heaton spatial statistics case study dataset. In the example, load_heaton is a unspecified function that reads in the dataset in the specified dict format. In practice, a user can use any conforming dataset. If one wants to predict on a univariate response as in this example, one must ensure the data is stored as a matrix rather than as a vector, i.e. that train['output'].shape = (train_count, 1). The regression API adds a sigma_sq scale parameter for the variance. One can set sigma_sq using the hyper_dict kwarg like other hyperparameters. The API expects that sigma_sq is a numpy.ndarray with a value associated with each dimension of the response, i.e. that train['output'].shape[1] == len(sigma_sq). In general, one should only manually set sigma_sq if they are certain they know what they are doing.

Regress on Heaton data with no variance

>> k_kwargs = { ... "kern": "rbf", ... "metric": "F2", ... "eps": {"val": 1e-5}, ... "nu": {"val": 0.38, "bounds": (0.1, 2.5)}, ... "length_scale": {"val": 7.2}, ... } >>> muygps, nbrs_lookup, predictions = do_regress( ... test_features, ... train_features, ... train_responses, ... nn_count=30, ... batch_size=200, ... loss_method="mse", ... variance_mode=None, ... k_kwargs=k_kwargs, ... nn_kwargs=nn_kwargs, ... verbose=True, ... ) parameters to be optimized: ['nu'] bounds: [[0.1 1. ]] sampled x0: [0.8858425] optimizer results: fun: 0.4797763813693626 hess_inv: <1x1 LbfgsInvHessProduct with dtype=float64> jac: array([-3.06976666e-06]) message: b'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL' nfev: 16 nit: 5 njev: 8 status: 0 success: True x: array([0.39963594]) NN lookup creation time: 0.04974837500000007s batch sampling time: 0.017116840000000022s tensor creation time: 0.14439213699999998s hyper opt time: 2.742181974s sigma_sq opt time: 5.359999999399179e-07s prediction time breakdown: nn time:0.1069446140000001s agree time:1.9999999967268423e-07s pred time:10.363597161000001s finds hyperparameters: nu : 0.8858424985399979 >>> print(f"mse : {mse_fn(predictions, test_responses)}") obtains mse: 2.345136495565052 ">
>>> import numpy as np
>>> from MuyGPyS.examples.regress import do_regress
>>> from MuyGPyS.optimize.objective import mse_fn
>>> train_features, train_responses = load_heaton_train()  # imaginary train getter
>>> test_features, test_responses = load_heaton_test()  # imaginary test getter
>>> nn_kwargs = {"nn_method": "exact", "algorithm": "ball_tree"}
>>> k_kwargs = {
...         "kern": "rbf",
...         "metric": "F2",
...         "eps": {"val": 1e-5},
...         "nu": {"val": 0.38, "bounds": (0.1, 2.5)},
...         "length_scale": {"val": 7.2},
... }
>>> muygps, nbrs_lookup, predictions = do_regress(
...         test_features,
...         train_features,
...         train_responses,
...         nn_count=30,
...         batch_size=200,
...         loss_method="mse",
...         variance_mode=None,
...         k_kwargs=k_kwargs,
...         nn_kwargs=nn_kwargs,
...         verbose=True,
... )
parameters to be optimized: ['nu']
bounds: [[0.1 1. ]]
sampled x0: [0.8858425]
optimizer results:
      fun: 0.4797763813693626
 hess_inv: <1x1 LbfgsInvHessProduct with dtype=float64>
      jac: array([-3.06976666e-06])
  message: b'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL'
     nfev: 16
      nit: 5
     njev: 8
   status: 0
  success: True
        x: array([0.39963594])
NN lookup creation time: 0.04974837500000007s
batch sampling time: 0.017116840000000022s
tensor creation time: 0.14439213699999998s
hyper opt time: 2.742181974s
sigma_sq opt time: 5.359999999399179e-07s
prediction time breakdown:
        nn time:0.1069446140000001s
        agree time:1.9999999967268423e-07s
        pred time:10.363597161000001s
finds hyperparameters:
        nu : 0.8858424985399979
>>> print(f"mse : {mse_fn(predictions, test_responses)}")
obtains mse: 2.345136495565052

If one requires the (individual, independent) posterior variances for each of the predictions, one can pass variance_mode="diagonal". This mode assumes that each output dimension uses the same model, and so will output an additional vector variance with a scalar posterior variance associated with each test point. The API also returns a (possibly trained) MuyGPyS.gp.MuyGPS or MuyGPyS.gp.MultivariateMuyGPS instance, whose sigma_sq member reports an array of multiplicative scaling parameters associated with the variance of each dimension. Obtaining the tuned posterior variance implies multiplying the returned variance by the scaling parameter along each dimension.

Regress on Heaton data while estimating diagonal variance

>> k_kwargs = { ... "kern": "rbf", ... "metric": "F2", ... "eps": {"val": 1e-5}, ... "nu": {"val": 0.38, "bounds": (0.1, 2.5)}, ... "length_scale": {"val": 7.2}, ... "sigma_sq": "learn", ... } >>> muygps, nbrs_lookup, predictions, variance = do_regress( ... test_features, ... train_features, ... train_responses, ... nn_count=30, ... batch_size=200, ... loss_method="mse", ... variance_mode="diagonal", ... k_kwargs=k_kwargs, ... nn_kwargs=nn_kwargs, ... verbose=False, ... ) >>> print(f"mse : {mse_fn(predictions, test_responses)}") obtains mse: 2.345136495565052 >>> print(f"diagonal posterior variance: {variance * muygps.sigma_sq()}") diagonal posterior variance: [0.52199482 0.45934382 0.81381388 ... 0.64982631 0.45958342 0.68602048] ">
>>> import numpy as np
>>> from MuyGPyS.examples.regress import do_regress
>>> from MuyGPyS.optimize.objective import mse_fn
>>> train_features, train_responses = load_heaton_train()  # imaginary train getter
>>> test_features, test_responses = load_heaton_test()  # imaginary test getter
>>> nn_kwargs = {"nn_method": "exact", "algorithm": "ball_tree"}
>>> k_kwargs = {
...         "kern": "rbf",
...         "metric": "F2",
...         "eps": {"val": 1e-5},
...         "nu": {"val": 0.38, "bounds": (0.1, 2.5)},
...         "length_scale": {"val": 7.2},
...	    "sigma_sq": "learn",
... }
>>> muygps, nbrs_lookup, predictions, variance = do_regress(
...         test_features,
...         train_features,
...         train_responses,
...         nn_count=30,
...         batch_size=200,
...         loss_method="mse",
...         variance_mode="diagonal",
...         k_kwargs=k_kwargs,
...         nn_kwargs=nn_kwargs,
...         verbose=False,
... )
>>> print(f"mse : {mse_fn(predictions, test_responses)}")
obtains mse: 2.345136495565052
>>> print(f"diagonal posterior variance: {variance * muygps.sigma_sq()}")
diagonal posterior variance: [0.52199482 0.45934382 0.81381388 ... 0.64982631 0.45958342 0.68602048]

Independent diagonal variance for each test item is the only form of posterior variance supported for a single model, and independent diagonal variance for each test item along each response dimension is the only form of posterior variacne supported for a multivariate model. Computing the full posterior covariance between the dimensions of multivariate output is not currently supported, but is planned for a future release. Computing the full posterior covariance between all inputs is not and will not be supported for scalability reasons.

Classification

What follows is an example workflow performing two-class classification with uncertainty quantification. Specific outputs uses a star-galaxy image dataset, where stars are labeled [-1, +1] and galaxies are labeled [+1, -1]. Loading logic is encapsulated in the imaginary load_stargal function. The workflow suffices for any conforming 2-class dataset.

What follows is example code surrounding the invocation of MuyGPyS.examples.classify.do_classify_uq. This function returns GP predictions surrogate_predictions and a list of index masks masks.

Run star-gal with UQ example instructions:

>> k_kwargs = { ... "kern": "rbf", ... "metric": "F2", ... "eps": {"val": 1e-5}, ... "nu": {"val": 0.38, "bounds": (0.1, 2.5)}, ... "length_scale": {"val": 7.2}, ... } >>> muygps, nbrs_lookup, surrogate_predictions, masks = do_classify_uq( ... test_features, ... train_features, ... train_labels, ... nn_count=30, ... opt_batch_size=200, ... uq_batch_size=500, ... loss_method="log", ... variance_mode=None, ... uq_objectives=example_lambdas, ... k_kwargs=k_kwargs, ... nn_kwargs=nn_kwargs, ... verbose=False, ... ) >>> accuracy, uq = do_uq(surrogate_predictions, test_labels, masks) >>> print(f"obtained accuracy: {accuracy}") obtained accuracy: 0.973... >>> print(f"mask uq : \n{uq}") mask uq : [[8.21000000e+02 8.53836784e-01 9.87144569e-01] [8.59000000e+02 8.55646100e-01 9.87528717e-01] [1.03500000e+03 8.66666667e-01 9.88845510e-01] [1.03500000e+03 8.66666667e-01 9.88845510e-01] [5.80000000e+01 6.72413793e-01 9.77972239e-01]] ">
>>> import numpy as np
>>> from MuyGPyS.examples.two_class_classify_uq import do_classify_uq, do_uq, example_lambdas
>>> from MuyGPyS.optimize.objective import mse_fn
>>> train_features, train_labels = load_stargal_train()  # imaginary train getter
>>> test_features, test_labels = load_stargal_test()  # imaginary test getter
>>> nn_kwargs = {"nn_method": "exact", "algorithm": "ball_tree"}
>>> k_kwargs = {
...         "kern": "rbf",
...         "metric": "F2",
...         "eps": {"val": 1e-5},
...         "nu": {"val": 0.38, "bounds": (0.1, 2.5)},
...         "length_scale": {"val": 7.2},
... }
>>> muygps, nbrs_lookup, surrogate_predictions, masks = do_classify_uq(
...         test_features,
...         train_features,
...         train_labels,
...         nn_count=30,
...         opt_batch_size=200,
...	    uq_batch_size=500,
...         loss_method="log",
...         variance_mode=None,
...	    uq_objectives=example_lambdas,
...         k_kwargs=k_kwargs,
...         nn_kwargs=nn_kwargs,
...         verbose=False,
... )
>>> accuracy, uq = do_uq(surrogate_predictions, test_labels, masks)
>>> print(f"obtained accuracy: {accuracy}")
obtained accuracy: 0.973...
>>> print(f"mask uq : \n{uq}")
mask uq : 
[[8.21000000e+02 8.53836784e-01 9.87144569e-01]
 [8.59000000e+02 8.55646100e-01 9.87528717e-01]
 [1.03500000e+03 8.66666667e-01 9.88845510e-01]
 [1.03500000e+03 8.66666667e-01 9.88845510e-01]
 [5.80000000e+01 6.72413793e-01 9.77972239e-01]]

uq_objectives expects a list of functions of alpha, beta, correct_count, and incorrect_count, where alpha and beta are the number of type I and type II errors, respectively. MuyGPyS.examples.classify.example_lambdas lists some options, but you can supply your own.

If uncertainty quantification is not desired, or the classifcation problem in question involves more than two classes, see instead an example workflow like that in MuyGPyS.examples.classify.do_classify.

Run MNIST without UQ example instructions:

>> k_kwargs = { ... "kern": "rbf", ... "metric": "F2", ... "eps": {"val": 1e-5}, ... "nu": {"val": 0.38, "bounds": (0.1, 2.5)}, ... "length_scale": {"val": 7.2}, ... } >>> muygps, nbrs_lookup, surrogate_predictions = do_classify( ... test_features, ... train_features, ... train_labels, ... nn_count=30, ... batch_size=200, ... loss_method="log", ... variance_mode=None, ... k_kwargs=k_kwargs, ... nn_kwargs=nn_kwargs, ... verbose=False, ... ) >>> predicted_labels = np.argmax(surrogate_predictions, axis=1) >>> true_labels = np.argmax(test_labels, axis=1) >>> accuracy = np.mean(predicted_labels == true_labels) >>> print(f"obtained accuracy: {accuracy}") 0.97634 ">
>>> import numpy as np
>>> from MuyGPyS.examples.classify import do_classify
>>> from MuyGPyS.optimize.objective import mse_fn
>>> train_features, train_labels = load_stargal_train()  # imaginary train getter
>>> test_features, test_labels = load_stargal_test()  # imaginary test getter
>>> nn_kwargs = {"nn_method": "exact", "algorithm": "ball_tree"}
>>> k_kwargs = {
...         "kern": "rbf",
...         "metric": "F2",
...         "eps": {"val": 1e-5},
...         "nu": {"val": 0.38, "bounds": (0.1, 2.5)},
...         "length_scale": {"val": 7.2},
... }
>>> muygps, nbrs_lookup, surrogate_predictions = do_classify(
...         test_features,
...         train_features,
...         train_labels,
...         nn_count=30,
...         batch_size=200,
...         loss_method="log",
...         variance_mode=None,
...         k_kwargs=k_kwargs,
...         nn_kwargs=nn_kwargs,
...         verbose=False,
... )
>>> predicted_labels = np.argmax(surrogate_predictions, axis=1)
>>> true_labels = np.argmax(test_labels, axis=1)
>>> accuracy = np.mean(predicted_labels == true_labels)
>>> print(f"obtained accuracy: {accuracy}")
0.97634

About

Authors

  • Benjamin W. Priest (priest2 at llnl dot gov)
  • Amanada L. Muyskens (muyskens1 at llnl dot gov)

Papers

MuyGPyS has been used the in the following papers (newest first):

  1. Gaussian Process Classification fo Galaxy Blend Identification in LSST
  2. Star-Galaxy Image Separation with Computationally Efficient Gaussian Process Classification
  3. Star-Galaxy Separation via Gaussian Processes with Model Reduction

Citation

If you use MuyGPyS in a research paper, please reference our article:

@article{muygps2021,
  title={MuyGPs: Scalable Gaussian Process Hyperparameter Estimation Using Local Cross-Validation},
  author={Muyskens, Amanda and Priest, Benjamin W. and Goumiri, Im{\`e}ne and Schneider, Michael},
  journal={arXiv preprint arXiv:2104.14581},
  year={2021}
}

License

MuyGPyS is distributed under the terms of the MIT license. All new contributions must be made under the MIT license.

See LICENSE-MIT, NOTICE, and COPYRIGHT for details.

SPDX-License-Identifier: MIT

Release

LLNL-CODE-824804

Comments
  • Relax scipy requirement to build on Python > 3.7

    Relax scipy requirement to build on Python > 3.7

    Scipy 1.4.1 appears to be incompatible with Python > 3.7. Relaxing the exact version requirement makes it possible to install MuyGPyS on newer versions of Python.

    opened by igoumiri 1
  • estimating sigma_sq using an rbf kernel appears to be broken

    estimating sigma_sq using an rbf kernel appears to be broken

    Trying to compute sigma_sq via MuyGPS.get_sigma_optim and get_analytic_sigma (for BenchmarkGP) appears to not be working, and will produce wrong results when trying to find a known sigma_sq value. See the test cases GPSigmaSqBaselineTest and GPSigmaSqOptimTest inside of tests/optimize.py. Using "l2" rather than "F2" to create the pairwise distances appears to solve the issue in the latter case, but I am at a loss as to why.

    opened by bwpriest 1
  • Feature/fast prediction

    Feature/fast prediction

    Added fast prediction feature to MuyGPs library. This PR includes a new test in tests/jax_correctness.py which ensures the fast predictions generated using Jax and numpy are nearly identical. More tests to be added to mpi_correctness.py, multivariate.py, gp.py, and api.py.

    opened by alecmdunton 0
  • Instrumenting test harness to different CI pipelines

    Instrumenting test harness to different CI pipelines

    Modified MuyGPyS._src.config.MuyGPySConfig so that can be modified by absl based upon CLI flags. This affords running the same tests more conveniently on different hardware platforms

    opened by bwpriest 0
  • Need wrapper for nearest neighbors and distance construction using LLNL/saltatlas.

    Need wrapper for nearest neighbors and distance construction using LLNL/saltatlas.

    We need to add LLNL/clippy-fied bindings to a nearest neighbors and distance tensor construction pipeline, once it is complete, to sit inside of MyGPyS.neighbors.NN_Wrapper. The final product will likely need to use LLNL/metall.

    opened by bwpriest 0
  • Need cross-entropy optimizer for `sigma_sq`

    Need cross-entropy optimizer for `sigma_sq`

    We need to add James' code for optimizing sigma_sq according to the cross entropy objective, described in his galaxy blend detection paper. This should be possible now that sigma_sq optimization has been modularized.

    opened by bwpriest 0
  • Need hierarchical nonstationary kernel implemented into `MuyGPyS`

    Need hierarchical nonstationary kernel implemented into `MuyGPyS`

    We need to add Amanda and Imène's hierarchical nonstationary kernel into the library and integrate it into all workflows and tests. It will most likely need to be reworked so that it meshes with the existing framework.

    opened by bwpriest 0
  • Need heteroscedastic noise (`eps`) parameter

    Need heteroscedastic noise (`eps`) parameter

    We need to support vector-valued eps parameters. Similar to MuyGPyS.gp.kernel.SigmaSq, we should break eps out of MuyGPyS.gp.kernel.Hyperparameter and make it its own class. We probably only want to support learning the epsilon parameter in the homoscedastic case, though, so we will need to maintain some guardrails to make sure that it is treated as a scalar where appropriate and as a vector where appropriate.

    opened by bwpriest 0
  • MuyGPyS._src.config.MuyGPySConfig depends upon JAX

    MuyGPyS._src.config.MuyGPySConfig depends upon JAX

    In v0.5.1, the config class inherits from JAX. However, this means that installing without JAX will throw an error. This will need to be fixed in the next update.

    opened by bwpriest 0
Releases(v0.6.5)
  • v0.6.5(Nov 22, 2022)

    v0.6.5 fixes a few small errors in v0.6.4 and updates the testing chassis. The change details are of little interest to users. Please see the release notes for v0.6.4 for details of the new features.

    Source code(tar.gz)
    Source code(zip)
  • v0.6.4(Nov 18, 2022)

    v0.6.4 introduces the fast mean prediction features described in [Dunton2022]. Notably, this fast mean prediction workflow is only supported in shared memory (numpy and JAX backends). Attempts to access the new functions while in MPI mode will raise NotImplementedErrors. Changes in detail include:

    • Added functions for creating the precomputed coefficient tensor for the fast regression feature
    • Added fast_regress() functions to MuyGPyS.gp.muygps.MuyGPS and MuyGPyS.gp.muygps.MultivariateMuyGPS
    • Added high level workflow for implementing fast regression in MuyGPyS.examples.fast_regress
    • Added new documentation notebook explaining the fast regression workflow
    • Added some performance improvements for MPI back end

    [Dunton2022] Dunton, Alec M., Benjamin W. Priest, and Amanda Muyskens. “Fast Gaussian Process Posterior Mean Prediction via Local Cross Validation and Precomputation.” arXiv preprint arXiv:2205.10879 (2022).

    Source code(tar.gz)
    Source code(zip)
  • v0.6.3(Oct 21, 2022)

    v0.6.3 is exactly the same as v0.6.1, but the fmfn/BayesianOptimization dependency is rolled back to the old behavior. ~This is because the maintainers of that project have lost control of the PyPI credentials, but their desired dependency management solution does not work when uploading to PyPI. Ergo, a user that needs a recent version of bayes_opt will need to manually update it in their environment.~ The maintainers fixed the PyPI version so the workaround is no longer necessary to get the newest version.

    Source code(tar.gz)
    Source code(zip)
  • v0.6.2(Oct 21, 2022)

    v0.6.2 is exactly the same as v0.6.1, but the fmfn/BayesianOptimization dependency is rolled back to the old behavior. This is because the maintainers of that project have lost control of the PyPI credentials, but their desired dependency management solution does not work when uploading to PyPI. Ergo, a user that needs a recent version of bayesian-optimization will need to manually update it in their environment.

    Source code(tar.gz)
    Source code(zip)
  • v0.6.1(Oct 21, 2022)

    v0.6.1 introduces a new loss function, the leave-one-out-likelihood loss, referred to throughout the library as "lool". Changes in detail:

    • Added new MuyGPyS.optimize.loss.lool_fn() and implementations.
    • Added "lool" as a supported loss_method option.
    • Modified MuyGPyS.gp.muygps.MuyGPS.get_opt_fn() -> MuyGPyS.gp.muygps.MuyGPS.get_opt_mean_fn() to accept an opt_method argument to specify which form of mean function to return
    • Added MuyGPyS.gp.muygps.MuyGPS.get_opt_var_fn() to return an unscaled variance function of form specified by opt_method argument.
    • Added some MPI documentation
    • Changed bayesian-optimization dependency to track from the github repo instead of PyPI per fmfn/BayesianOptimization#366.
    Source code(tar.gz)
    Source code(zip)
  • v0.6.0(Oct 12, 2022)

    v0.6.0 introduces support for distributed memory processing using MPI. MuyGPyS now supports three distinction implementations of all of the math functions. The version to be used is determined at import time, in the same way as JAX support as introduced in v0.5.0. Some MPI feature details:

    • pip installation now supports the additional mpi extras flag to install the python mpi bindings. However, the user must manually install MPI in their environment. MuyGPyS does not and will not do this for the end user. Please refer to the README for clarification.
    • Currently, if JAX dependencies and bindings are found they will supercede the MPI bindings by default. This can be overridden by modifying the MuyGPyS.config object or passing absl arguments at the command line. Future releases will support options to simultaneously use MPI and JAX.
    • Notably, the various implementations (numpy, JAX, and MPI) of MuyGPyS include only the kernel math and optimization functions. MuyGPyS.NN_Wrapper current wraps third-party libraries, and so the nearest neighbors computations do not take advantage of distributed memory or hardware acceleration. This may change in future releases.
    • Just like the JAX implementations, the MPI implementations of MuyGPyS functions share the same API as the single-core numpy implementation. Thus, existing workflows should trivially generalize to distributed memory (less nearest neighbors and sampling portions). However, the MPI implementation partitions data across all available processors. This means that querying said data outside of MuyGPyS functions (e.g. for visualization purposes) will require the user to use MPI directly.

    There are also a number of quality-of-life and future-proofing changes to the MuyGPyS API. Changes in detail:

    • MuyGPyS.gp.muygps.MuyGPS.sigma_sq_optim() is removed. Independent sigam_sq optimization can now be accomplished with MuyGPyS.optimize.sigma_sq.muygps_sigma_sq_optim(). The function has a similar signature, except that it (1) accepts a MuyGPS object as an argument and returns a new MuyGPS object with an optimized sigma_sq parameter, and (2) uses an nn_targets tensor instead of nn_indices + targets matrices, which is a necessary change for the distributed memory workflows.
    • MuyGPyS.gp.muygps.MuyGPS.get_opt_fn() and MuyGPyS.gp.kernel.KernelFn.get_opt_fn() now require an opt_method argument that specifies which format the returned optimization functions should support.
    • Loss functions are moved out of MuyGPyS.optimize.objective and into the new MuyGPyS.optimize.loss.
    • Objective function choice is now modular. MuyGPyS.optimize.objective.make_obj_fn() is now the function to used to construct an objective function. In addition to the original arguments, it now expects two new arguments: (1) obj_method specifies the form of the objective function, and currently only supports "loo_crossval" but will support other options in the future, and (2) opt_method specifies the format of the optimizers, and currently supports "scipy" and "bayes". obj_method is now a kwarg in the high-level example workflows.
    • The default value of opt_method has changed from "scipy" to "bayes" throughout the project.
    Source code(tar.gz)
    Source code(zip)
  • v0.5.2(Apr 20, 2022)

    v0.5.2 fixes a critical bug introduced in v0.5.1. It is now possible to import MuyGPyS without installing JAX, as intended. There are no other semantic changes to the code, so all features of v0.5.1 are preserved. Changes in detail:

    • Added MuyGPyS._src.jaxconfig.Config, which provides a local copy of jax._src.config.Config for the inheritance of MuyGPyS._src.config.MuyGPySConfig in case jax is not installed.
    Source code(tar.gz)
    Source code(zip)
  • v0.5.1(Mar 23, 2022)

    v0.5.1 adds support for batch optimization of hyperparameters using BayesianOptimization. Changes in detail:

    • High-level optimization functions now support an opt_method kwarg that accepts "scipy" and "bayesian" (alternately "bayes" or "bayes_opt") options.
    • High-level optimization functions now forward additional kwargs to the optimization method. This is not relevant for scipy, but can drastically affect performance using BayesianOptimization. See the documentation notebooks for examples.
    • MuyGPyS.optimize.chassis.scipy_optimize_from_tensors() is now deprecated and will be removed in the future. Instead use MuyGPyS.optimize.chassis.optimize_from_tensors() with the kwarg opt_method="scipy". The same is true for *_from_indices.
    • Significantly changed how the MuyGPyS.config object works. See the updated README and documentation.
    • Fixed a simple but major SigmaSq value assignment bug.
    • Fixed a minor bug related to optimizing epsilon.
    Source code(tar.gz)
    Source code(zip)
  • v0.5.0(Mar 1, 2022)

    v0.5.0 introduces just-in-time compilation and GPU support using JAX. This change allows for the acceleration of workflows on CPU (1-2.5x) and NVidia GPU (30-60x). The code API is unchanged from v0.4.1 - all codes should still work. The only major changes for the user surround installation.

    Briefly, pip installation (from either PyPI or source) now uses extras flags to manage optional dependencies - see the README for supported optional flags and their effects. Installing MuyGPyS in an environment without JAX (e.g. pip install muygpys) will result in the use of numpy implementations for all math functions. On CPU, pip install muygpys[jax_cpu] will install the JAX dependencies. GPU installation is more complicated; please refer to the README.

    Although JAX operates by default on 32 bit types, we force it by default to use 64 bit types in order to maintain agreement with the numpy implementations (up to machine precision). The user can override this and use 32 bit types for faster computation. See the README for details.

    Source code(tar.gz)
    Source code(zip)
  • v0.4.1(Jan 19, 2022)

    v0.4.1 streamlines the codebase by moving all of the computation inside of pure functions. This will hopefully improve readability and changeability of the codebase for future development. The object-oriented API remains largely unchanged; member functions are now wrappers around static pure functions. The only breaking API changes are to MuyGPyS.optimize.objective.loo_crossval(), which is most likely masked by MuyGPyS.optimize.chassis.scipy_optimize_from_tensors() to most users. The latter function now returns an optimized model instead of modifying the given model in place. Major changes are as follows:

    • Gave SigmaSq a trained() boolean member function to check whether it has been set. No longer assumes "unlearned" values.
    • Modified optimization chassis to use pure functions. scipy_optimize_from_* functions now return an optimized model.
    • Moved opt function preparation into KernelFn and MuyGPS classes.
    • Hyperparameter bounds no longer take the value "fixed". They instead default to (0.0, 0.0). The fixed status of hyperparameters is now accessed via Hyperparameter.fixed().
    • Relaxed dependency scipy>=1.4.1 and incremented hnswlib>=0.6.0.
    Source code(tar.gz)
    Source code(zip)
  • v0.4.0(Dec 9, 2021)

    v0.4.0 overhauls the handling of the sigma_sq parameter throughout the code based upon user feedback. These changes will hopefully reduce confusion surrounding how we've implemented this scaling parameter in the past. Major changes are as follows:

    • sigma_sq is segregated from the usual hyperparameter handling. Users can no longer set sigma_sq or provide bounds (which were ignored in old versions anyway), and must train it directly. Currently the only method for doing so uses the analytic approximation in MuyGPS.sigma_sq_optim().
    • do_regress() gains the sigma_method kwarg, whose default argument "analytic" automates the above process. The only other accepted value None results in not training sigma_sq, which mostly arises in classification or settings where the user does not need variance.
    • do_regress() (as well as MuyGPS.regress() and related functions) gain the apply_sigma_sq boolean kwarg, whose default value True results in automatically scales the predicted variance using sigma_sq.
    • do_regress() gains the return_distances boolean kwarg (default False). If true, the API call will return the crosswise and pairwise distances of the test data and its nearest neighbor sets as additional return values. This allows the user to retain the distance tensors for later use, if so desired.
    • Added convenience functions MuyGPyS.gp.distance.make_regress_tensors() and MuyGPyS.gp.distance.make_train_tensors() that provide a simpler interface to simultaneously create the crosswise_dists, pairwise_dists, batch_nn_targets, and batch_targets (latter only).
    • MuyGPyS.testing.gp.BaselineGP now uses the current kernel API.
    • Tutorial boilerplate has moved out of the README and into jupyter notebooks stored in docs/examples/. These notebooks also compile into pages on the readthedocs.io web documentation using nbsphinx.
    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Jul 29, 2021)

    Stable prerelease version 0.3.0. Includes support for singleton and multivariate MuyGPs models for both regression and classification, and includes some support for computing the posterior variance and uncertainty quantification tuning, although more features along these lines are planned in future releases. Full documentation included.

    Source code(tar.gz)
    Source code(zip)
Owner
Lawrence Livermore National Laboratory
For more than 65 years, the Lawrence Livermore National Laboratory has applied science and technology to make the world a safer place.
Lawrence Livermore National Laboratory
Using Bayesian, KNN, Logistic Regression to classify spam and non-spam.

Make Sure the dataset file "spamData.mat" is in the folder spam\src Environment: Python --version = 3.7 Third Party: numpy, matplotlib, math, scipy

0 Dec 26, 2021
Nature-inspired algorithms are a very popular tool for solving optimization problems.

Nature-inspired algorithms are a very popular tool for solving optimization problems. Numerous variants of nature-inspired algorithms have been develo

NiaOrg 215 Dec 28, 2022
Search algorithm implementations meant for teaching

Search-py A collection of search algorithms for teaching and experimenting. Non-adversarial Search There’s a heavy separation of concerns which leads

Dietrich Daroch 5 Mar 07, 2022
Gnat - GNAT is NOT Algorithmic Trading

GNAT GNAT is NOT Algorithmic Trading! GNAT is a financial tool with two goals in

Sher Shah 2 Jan 09, 2022
Algorithmic trading backtest and optimization examples using order book imbalances. (bitcoin, cryptocurrency, bitmex)

Algorithmic trading backtest and optimization examples using order book imbalances. (bitcoin, cryptocurrency, bitmex)

172 Dec 21, 2022
Genetic Algorithm for Robby Robot based on Complexity a Guided Tour by Melanie Mitchell

Robby Robot Genetic Algorithm A Genetic Algorithm based Robby the Robot in Chapter 9 of Melanie Mitchell's book Complexity: A Guided Tour Description

Matthew 2 Dec 01, 2022
Python sample codes for robotics algorithms.

PythonRobotics Python codes for robotics algorithm. Table of Contents What is this? Requirements Documentation How to use Localization Extended Kalman

Atsushi Sakai 17.2k Jan 01, 2023
Fedlearn algorithm toolkit for researchers

Fedlearn algorithm toolkit for researchers

89 Nov 14, 2022
Better control of your asyncio tasks

quattro: task control for asyncio quattro is an Apache 2 licensed library, written in Python, for task control in asyncio applications. quattro is inf

Tin Tvrtković 37 Dec 28, 2022
Zipline, a Pythonic Algorithmic Trading Library

Zipline, a Pythonic Algorithmic Trading Library

Stefan Jansen 463 Jan 08, 2023
A lightweight, object-oriented finite state machine implementation in Python with many extensions

transitions A lightweight, object-oriented state machine implementation in Python with many extensions. Compatible with Python 2.7+ and 3.0+. Installa

4.7k Jan 01, 2023
Machine Learning algorithms implementation.

Machine Learning Algorithms Machine Learning algorithms implementation. What can I find here? ML Algorithms KNN K-Means-Clustering SVM (MultiClass) Pe

David Levin 1 Dec 10, 2021
Algoritmos de busca:

Algoritmos-de-Buscas Algoritmos de busca: Abaixo está a interface da aplicação: Ao selecionar o tipo de busca e o caminho, então será realizado o cálc

Elielson Barbosa 5 Oct 04, 2021
Given a list of tickers, this algorithm generates a recommended portfolio for high-risk investors.

RiskyPortfolioGenerator Given a list of tickers, this algorithm generates a recommended portfolio for high-risk investors. Working in a group, we crea

Victoria Zhao 2 Jan 13, 2022
QDax is a tool to accelerate Quality-Diveristy (QD) algorithms through hardware accelerators and massive parallelism

QDax: Accelerated Quality-Diversity QDax is a tool to accelerate Quality-Diveristy (QD) algorithms through hardware accelerators and massive paralleli

Adaptive and Intelligent Robotics Lab 183 Dec 30, 2022
A custom prime algorithm, implementation, and performance code & review

Colander A custom prime algorithm, implementation, and performance code & review Pseudocode Algorithm 1. given a number of primes to find, the followi

Finn Lancaster 3 Dec 17, 2021
A* (with 2 heuristic functions), BFS , DFS and DFS iterativeA* (with 2 heuristic functions), BFS , DFS and DFS iterative

Descpritpion This project solves the Taquin game (jeu de taquin) problem using different algorithms : A* (with 2 heuristic functions), BFS , DFS and D

Ayari Ahmed 3 May 09, 2022
🌟 Python algorithm team note for programming competition or coding test

🌟 Python algorithm team note for programming competition or coding test

Seung Hoon Lee 3 Feb 25, 2022
Cormen-Lib - An academic tool for data structures and algorithms courses

The Cormen-lib module is an insular data structures and algorithms library based on the Thomas H. Cormen's Introduction to Algorithms Third Edition. This library was made specifically for administeri

Cormen Lib 12 Aug 18, 2022