Library / SDK
CyberAgentAILab/cmaes avatar
CyberAgentAILab/cmaes

cmaes is a small Python library for CMA-ES and its mixed variable variants

Python library for CMA Evolution Strategy.

517 stars78 forksPythonMIT

At a glance

What is it?
cmaes implements the covariance matrix adaptation evolution strategy in Python with an ask-and-tell interface, and it has grown variants for the cases plain CMA-ES handles badly: CatCMAwM for mixed continuous, integer and categorical variables, and COMO-CatCMAwM for two-objective versions of the same. The dependency list is numpy, and scipy is an extra.
Who is it for?
Adopt cmaes when you are optimising a black box with continuous parameters and want a well understood derivative free method without a heavy framework, and reach for CatCMAwM when some of your variables are integers or categories. Do not adopt it if you have gradients to hand, since a gradient method will beat it on a smooth problem, and note that the multi-objective variant currently supports two objectives only, with three or more planned.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Ask and tell, and why that shape

The library is described as a simple and practical Python library for CMA-ES, and the paper is now available on arXiv as 2402.01373, with the README referring to Nomura et al. 2026 for the detailed information, including the design philosophy and advanced examples.

The interface is the ask-and-tell style. You ask the optimiser for candidate solutions, evaluate them yourself, and tell it the values:

python
import numpy as np
from cmaes import CMA

def quadratic(x1, x2):
    return (x1 - 3) ** 2 + (10 * (x2 + 2)) ** 2

if __name__ == "__main__":
    optimizer = CMA(mean=np.zeros(2), sigma=1.3)

    for generation in range(50):
        solutions = []
        for _ in range(optimizer.population_size):
            x = optimizer.ask()
            value = quadratic(x[0], x[1])
            solutions.append((x, value))
            print(f"#{generation} {value} (x1={x[0]}, x2 = {x[1]})")
        optimizer.tell(solutions)

That shape is the important design decision. The optimiser never calls your function, so the evaluation can be anything: a training run, a simulation, a query to a service, a game. It also means the evaluation can be parallel, because you collect a whole generation of candidates and then hand them back.

The implementation is the standard version of CMA-ES, from Hansen 2016, which matters because CMA-ES has a lot of accumulated practical tuning, and the value of a from-scratch implementation is that it follows the reference rather than improvising.

The initialisation is the familiar pair, a mean vector and a sigma, the initial standard deviation. `optimizer.population_size` tells you how many candidates to ask for, and the CMA-ES default population size grows with the number of dimensions, which is the algorithm's answer to how many samples you need in a high dimensional space.

The package is Python, MIT licensed, with numpy as the only required dependency, and it supports Python 3.9 and later.

Mixed variables with CatCMAwM

The most useful thing in the library is the variant that handles the cases plain CMA-ES cannot, and the recommendation about it is blunt.

CatCMA with Margin, CatCMAwM, is a method for mixed-variable optimization problems, simultaneously optimizing continuous, integer and categorical variables. It extends CatCMA by introducing a novel integer handling mechanism, and supports arbitrary combinations of continuous, integer and categorical variables in a unified framework.

The README recommends using CatCMAwM for continuous plus integer and continuous plus categorical settings, and says that Hamano et al. 2025 shows CatCMAwM outperforms CMA-ES with Margin in mixed-integer scenarios. Therefore it suggests CatCMAwM in place of CMA-ES with Margin or CatCMA.

The example makes the shape of the API clear. You declare the space of each variable type, the continuous variables as lower and upper bound pairs, the integer variables as a list of possible values, and the categorical variables as a count of categories, and then the ask result carries three components, `sol.x`, `sol.z` and `sol.c`:

python
import numpy as np
from cmaes import CatCMAwM


def SphereIntCOM(x, z, c):
    return sum(x * x) + sum(z * z) + len(c) - sum(c[:, 0])


def f_cont_int_cat():
    # [lower_bound, upper_bound] for each continuous variable
    X = [[-5, 5], [-5, 5]]
    # possible values for each integer variable
    Z = [[-1, 0, 1], [-2, -1, 0, 1, 2]]
    # number of categories for each categorical variable
    C = [3, 3]

    optimizer = CatCMAwM(x_space=X, z_space=Z, c_space=C)

    for generation in range(50):
        solutions = []
        for _ in range(optimizer.population_size):
            sol = optimizer.ask()
            value = SphereIntCOM(sol.x, sol.z, sol.c)
            solutions.append((sol, value))
            print(f"#{generation} {sol} evaluation: {value}")
        optimizer.tell(solutions)

The mechanism behind the categorical handling is visible in the example code. A categorical variable is represented as a probability vector over its categories, with the comment noting that each row must sum to 1, and the initialisation zero-pads at the end, so the number of categories is the number of columns. A solution's categorical component is therefore a distribution rather than a label, and the objective is expected to handle it, which is why the example objectives take the whole `c` array and index into it rather than receiving a single category.

That is a real modelling decision rather than a detail: a categorical variable is optimised in the relaxed space and the decoding to a label happens at evaluation time.

The integer variables are different. The `z_space` lists the allowed values directly, for example `[-1, 0, 1]`, which means the integer handling is exact rather than a rounded continuous value, and that is the novel integer handling mechanism the README mentions.

Two objectives, and the multi-objective variant

COMO-CatCMA with Margin performs multi-objective mixed-variable optimization by coordinating multiple CatCMAwM optimizers. It currently supports two-objective problems, and support for three or more objectives is planned.

The example shows how the objectives are formed, and it is a different pattern from the single objective case. The objective function takes the continuous part, the integer part, the categorical part and the number of categories, and returns a list of two values. The first objective combines a continuous sphere term and an integer sphere term with a term derived from the cumulative product of the category indicator, and the second does the same with the offset sphere and the reversed cumulative product.

python
import numpy as np
from cmaes import COMOCatCMAwM


def DSIntLFTL(x, z, c, cat_num):
    Sphere1 = sum((x / 10) ** 2) / len(x)
    Sphere2 = sum((x / 10 - 1) ** 2) / len(x)
    SphereInt1 = sum((z / 10) ** 2) / len(z)
    SphereInt2 = sum((z / 10 - 1) ** 2) / len(z)
    c_idx = c.argmax(axis=1)
    LF = (len(c) - (c_idx == 0).cumprod().sum()) / len(c)
    TL = (len(c) - (c_idx == np.asarray(cat_num) - 1)[::-1].cumprod().sum()) / len(c)
    obj1 = Sphere1 + SphereInt1 + LF
    obj2 = Sphere2 + SphereInt2 + TL
    return [obj1, obj2]

The two-objective limit is a real constraint rather than a footnote, and it is the kind of thing that changes whether the library fits your problem. Pareto front methods get complicated fast beyond two objectives, and the coordination of several CatCMAwM optimisers is where that complexity lands.

The full source is available in the examples directory, at `examples/como_catcma_with_margin.py`, and the same directory holds the other variants, including `catcma.py`, `catcma_with_margin.py`, `cma_with_margin_binary.py` and `cma_with_margin_integer.py`.

That examples directory is worth browsing as a whole, because it is where the library's range shows. There are `bipop_cma.py` and `ipop_cma.py` for the restarts that handle the rugged and high dimensional cases, `safecma.py` and `sep_cma.py` for the separable and safe variants, `lra_cma.py`, `mapcma.py`, `cma_sop.py` and `ws_cma.py`, an `optuna_sampler.py`, and simple function examples such as `quadratic_2d_function.py` and `ellipsoid_function.py`.

Using it through Optuna, and the packaging

The library also plugs into Optuna, which is the route to take if you already have an Optuna study and want CMA-ES as one sampler among several:

python
import optuna

def objective(trial: optuna.Trial):
    x1 = trial.suggest_float("x1", -4, 4)
    x2 = trial.suggest_float("x2", -4, 4)
    return (x1 - 3) ** 2 + (10 * (x2 + 2)) ** 2

if __name__ == "__main__":
    sampler = optuna.samplers.CmaEsSampler()
    study = optuna.create_study(sampler=sampler)
    study.optimize(objective, n_trials=250)

The Optuna integration is on the topics list as well, and the Optuna documentation page for the CmaEsSampler is the reference for it.

The packaging is minimal by design. The build backend is setuptools with `setuptools>=77`, the project name is `cmaes`, and the description is a lightweight Covariance Matrix Adaptation Evolution Strategy implementation for Python 3. The version is dynamic, taken from `cmaes.__version__`, and the packages list is just `cmaes`, with `include-package-data = false`.

The single required dependency is numpy. There is one optional extra, `cmawm = ["scipy"]`, which is the mixed variable variant, so the extra is named after the feature that needs it rather than after the module.

The project classifiers give the version support and the maturity: Python 3.9 through 3.14, and a development status of 4, Beta. The author is Masashi Shibata, and the maintainers are Masahiro Nomura, Ryoki Hamano, Kazuhiro Ota and Naoki Hamada, all with the CyberAgentAILab connection.

The repository layout is small, with `cmaes/` for the package, `tests/`, `benchmark/`, `tools/`, `examples/` and a `fuzzing.py` at the root, which is a detail worth noting, since fuzzing the parser of a numeric library is a reasonable thing to do and the presence of the file says someone did.

Where it fits, and where it does not

The alternatives to CMA-ES in this space fall into two groups, and the choice depends on what you know about your objective.

If you have gradients, use a gradient method. CMA-ES is a derivative free method, and on a smooth objective with a cheap gradient, a gradient descent or a quasi-Newton method will reach the optimum in a fraction of the evaluations. CMA-ES earns its cost when the objective is a black box: a training run with a non-differentiable pipeline, a simulation, a physical experiment, a rendering.

The second alternative is a general purpose hyperparameter optimisation framework such as Optuna, with CMA-ES as one of its samplers. The difference in approach is that the framework owns the study, the storage, the visualisation and the pruning, and the sampler is one component inside it, so you get comparability across samplers and a dashboard, at the cost of the framework's data model and its own opinions about the search space.

Using cmaes directly is the opposite: you own the loop, the objective and the data, and there is nothing between your function and the optimiser. For a search you run inside a larger experiment, that is the right shape.

The limits of the library itself are worth stating. The standard CMA-ES is continuous only, and the mixed variable variants exist because of that, so if your variables are categorical with many levels, the relaxed representation has to be handled by your objective, as the example code shows. The multi-objective variant handles two objectives and not three. The beta status means the API may still move.

And the practical limit that applies to every CMA-ES user is evaluation cost. The algorithm is efficient in the number of evaluations, not in wall clock time, and the population size grows with dimensionality, so a 200 dimensional problem asks for a lot of candidates per generation. If each evaluation is a day of compute, the arithmetic does not work, and the right answer is a surrogate model rather than a better sampler.

Editorial conclusion

Adopt cmaes when you are optimising a black box with continuous parameters and want a well understood derivative free method without a heavy framework, and reach for CatCMAwM when some of your variables are integers or categories. Do not adopt it if you have gradients to hand, since a gradient method will beat it on a smooth problem, and note that the multi-objective variant currently supports two objectives only, with three or more planned. Verify first that the objective you optimise is the kind CMA-ES is good at, that is continuous and cheap to evaluate, that you install the `cmawm` extra with scipy if you use the mixed variable variant, and that the version you take is v0.13.1 from 2026-08-21, with the last push on 2026-10-20.

Frequently asked questions

What is the cmaes Python library?

It is a lightweight Python implementation of the Covariance Matrix Adaptation Evolution Strategy, using the standard version from Hansen 2016 and offering an ask-and-tell interface where you evaluate the candidates yourself. The project is MIT licensed, depends only on numpy, and supports Python 3.9 and later.

How do I use CMA-ES with cmaes?

Create an optimiser with `CMA(mean=np.zeros(2), sigma=1.3)`, then in each generation call `optimizer.ask()` for a candidate, evaluate it, collect the pairs and pass them to `optimizer.tell(solutions)`. The README's example optimises a two dimensional quadratic function over 50 generations.

What is CatCMAwM and when should I use it?

CatCMA with Margin is for mixed-variable problems, optimising continuous, integer and categorical variables together in a unified framework. The README recommends it over CMA-ES with Margin and over CatCMA for continuous plus integer and continuous plus categorical settings, citing Hamano et al. 2025.

Can cmaes optimise categorical and integer variables?

Yes, through the CatCMAwM class. You declare the continuous variables as bound pairs in `x_space`, the integer variables as lists of allowed values in `z_space`, and the categorical variables as a count of categories in `c_space`, and the ask result carries the three components `sol.x`, `sol.z` and `sol.c`.

Does cmaes support multi-objective optimisation?

Yes, through COMO-CatCMAwM, which coordinates multiple CatCMAwM optimizers for multi-objective mixed-variable problems. It currently supports two-objective problems, with support for three or more objectives planned.

Official sources

  1. CyberAgentAILab/cmaes on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/cyberagentailab-cmaes.svg)](https://hysenlabs.com/projects/cyberagentailab-cmaes)

Was this useful?