From 2608726a11913941880f12862de88d08f872a1d5 Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 14 Nov 2025 16:46:15 +0100 Subject: [PATCH 01/14] feat(problem): add support for Sobol sequences sampling --- ksos_tools/solvers/problem.py | 2 +- ksos_tools/utils.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ksos_tools/solvers/problem.py b/ksos_tools/solvers/problem.py index d52c892..a361d81 100644 --- a/ksos_tools/solvers/problem.py +++ b/ksos_tools/solvers/problem.py @@ -254,7 +254,7 @@ def generate_new_samples( ): radius = np.array([radius] * dim) assert n_samples >= 1 - assert sampling in ["linspace", "uniform"] + assert sampling in ["linspace", "uniform", "sobol"] # Generate samples if sampling_function is not None: diff --git a/ksos_tools/utils.py b/ksos_tools/utils.py index e513dc1..35ac1da 100644 --- a/ksos_tools/utils.py +++ b/ksos_tools/utils.py @@ -1,6 +1,6 @@ import cvxpy as cp import numpy as np - +from scipy.stats import qmc def get_samples(center, radius, n_samples, sampling): dim = len(center) @@ -27,6 +27,14 @@ def get_samples(center, radius, n_samples, sampling): for i in range(dim) ] ).T + elif sampling == "sobol": + sampler = qmc.Sobol(d=dim, scramble=True) + samples_unit = sampler.random(n_samples) + samples = qmc.scale( + samples_unit, + l_bounds=center - radius, + u_bounds=center + radius, + ) else: raise ValueError(f"Unknown sampling function: {sampling}") return samples From 55dd494bd119e4a6361e3a416a11e8a386830713 Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 5 Dec 2025 15:10:34 +0100 Subject: [PATCH 02/14] feat(problem): add periodic kernel --- ksos_tools/solvers/ksos.py | 10 +++++++--- ksos_tools/solvers/problem.py | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ksos_tools/solvers/ksos.py b/ksos_tools/solvers/ksos.py index 7dcf950..83b0be5 100644 --- a/ksos_tools/solvers/ksos.py +++ b/ksos_tools/solvers/ksos.py @@ -20,6 +20,7 @@ def solve( radius: np.ndarray | float | None = None, n_samples: int | None = None, samples: np.ndarray | None = None, + f_samples: np.ndarray | None = None, sampling: str = "uniform", sampling_function: Callable[[], np.ndarray] | None = None, lambd: float = 1e-3, @@ -101,7 +102,7 @@ def solve( radius = np.array([radius] * dim) assert n_samples is None or n_samples >= 1 assert lambd >= 0 - assert sigma > 0 + assert (kernel == "Periodic" and isinstance(sigma, tuple) and sigma[0] > 0 and sigma[1] > 0) or sigma > 0 assert warm_iterations >= 1 if samples is not None: assert np.ndim(samples) >= 2 @@ -158,7 +159,10 @@ def solve( # generate samples and kernel matrix if samples is not None: - problem.register_fixed_samples(samples, f) + if f_samples is not None: + problem.register_fixed_samples(samples, None, f_samples) + else: + problem.register_fixed_samples(samples, f, None) if solver != "naive": success = problem.initialize_kernel( @@ -279,7 +283,7 @@ def solve( radius = radius * d if verbose: print(f"Sobolev norm: {norm} | Decay: {d} | New radius: {radius}") - else: + elif iteration < warm_iterations - 1: assert radius is not None assert sigma is not None assert isinstance(decay, float) diff --git a/ksos_tools/solvers/problem.py b/ksos_tools/solvers/problem.py index a361d81..57f7f8c 100644 --- a/ksos_tools/solvers/problem.py +++ b/ksos_tools/solvers/problem.py @@ -43,6 +43,9 @@ def kernel_function(x, y, sigma, kernel): return np.exp(-np.linalg.norm(x - y) ** 2 / (2 * sigma**2)) elif kernel == "Polynomial": return (1 + np.inner(x, y)) ** int(sigma) + elif kernel == "Periodic": + p, l = sigma + return np.prod(np.exp(-2 * (np.sin(np.pi * (x - y) / p) ** 2) / l**2)) else: raise ValueError(f"unknown kernel function {kernel}") From d5dde117216bd4a1828312cc4f7e0262580732ac Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 2 Jan 2026 14:33:48 +0100 Subject: [PATCH 03/14] chore: apply fmt --- README.md | 4 ++-- example.py | 26 +++++++++++++------------- ksos_tools/utils.py | 1 + 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ef503d0..e83e22c 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ ![](overview.png) -# KernelSOS Toolbox +# Kernel Sum-of-Squares (KernelSOS) Toolbox Implementation of the Kernel Sum-of-Squares optimization framework and various related solvers. The kernelSOS algorithm solves the following general optimization problem: ```math \min_{x\in\Omega} f(x) ``` -where $f$ is any function that can be sampled over the convex bouded domain $\Omega$. Its generality allows it to be applied to a wide range of problems. +where $f$ is any function that can be sampled over the convex bounded domain $\Omega$. Its generality allows it to be applied to a wide range of problems. ## Usage The main solver is implemented as `ksos_tools.solvers.ksos.solve`. A minimal example of how to use it is as follows (see `example.py`): diff --git a/example.py b/example.py index 2e574eb..b34993c 100644 --- a/example.py +++ b/example.py @@ -2,24 +2,24 @@ import numpy as np # Standard parameters -center = [0.0] # center for sampling -radius = np.pi # radius of sampling -sampling = "linspace" # use a uniform grid for sampling -n_samples = 10 # how many samples to use +center = [0.0] # center for sampling +radius = np.pi # radius of sampling +sampling = "linspace" # use a uniform grid for sampling +n_samples = 10 # how many samples to use -# Which solver to use: can be MOSEK, newton (simple log-barrier Newton +# Which solver to use: can be MOSEK, newton (simple log-barrier Newton # solver), newton-features or newton-kernel (more advanced solvers using # feature or kernel matrices, respectively; adding e.g. a linesearch option -# and more advanced diagnostics to the original solver. -solver = "newton" +# and more advanced diagnostics to the original solver. +solver = "newton" -# Which kernel to use: use Gauss for smooth, Laplace for less smooth, -# or provide a kernel of your choice. -kernel = "Gauss" +# Which kernel to use: use Gauss for smooth, Laplace for less smooth, +# or provide a kernel of your choice. +kernel = "Gauss" # Kernel parameter: good to use roughly the minimum distance between -# samples. -sigma = 2 * np.pi / n_samples +# samples. +sigma = 2 * np.pi / n_samples solution, info = ksos.solve( f=np.sin, @@ -29,7 +29,7 @@ center=center, radius=radius, solver=solver, - sigma=sigma + sigma=sigma, ) print(f"Found solution: x={solution[0]:.4f}, f={info['cost']:.4f}") print(f"True solution: x={-np.pi/2:.4f}, f=-1") diff --git a/ksos_tools/utils.py b/ksos_tools/utils.py index 35ac1da..859f63a 100644 --- a/ksos_tools/utils.py +++ b/ksos_tools/utils.py @@ -2,6 +2,7 @@ import numpy as np from scipy.stats import qmc + def get_samples(center, radius, n_samples, sampling): dim = len(center) if isinstance(radius, float) or isinstance(radius, int): From 59d27b1f4277a92c9563a64ee3053a1b45a3aefc Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 2 Jan 2026 14:38:12 +0100 Subject: [PATCH 04/14] feat(README): add citation for software and paper --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index e83e22c..00d034c 100644 --- a/README.md +++ b/README.md @@ -50,3 +50,29 @@ print(f"True solution: x={-np.pi/2:.4f}, f=-1") ``` A large variety of options can be passed to the solver, and can be found in the [`ksos.solve`](ksos_tools/solvers/ksos.py) function documentation. Many examples are showcased in the `tests` directory. + +## Citation +If you use this code in your research, please use the following citation: +```bibtex +@software{ksos_tools, + author = {Groudiev, Antoine and Dümbgen, Frederike}, + title = {{ksos-tools}: Implementation of the {Kernel Sum-of-Squares} optimization framework and various related solvers}, + url = {https://github.com/Simple-Robotics/ksos-tools}, + version = {0.2.2}, + date = {2025-11-14}, +} +``` + +You can also cite the following related paper: + +```bibtex +@misc{groudiev2025ksos, + title={Sampling-Based Global Optimal Control and Estimation via Semidefinite Programming}, + author={Groudiev, Antoine and Schramm, Fabian and Berthier, Éloïse and Carpentier, Justin and Dümbgen, Frederike}, + year={2025}, + eprint={2507.17572}, + archivePrefix={arXiv}, + primaryClass={cs.RO}, + url={https://arxiv.org/abs/2507.17572}, +} +``` \ No newline at end of file From 8b82fdad6ad184befe4d455c72b73e0844a6177f Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 2 Jan 2026 17:16:01 +0100 Subject: [PATCH 05/14] chore(pyproject): update repository link and description --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6998bd6..ad1e0ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,10 +9,10 @@ authors = [ { name = "Antoine Groudiev", email = "antoine.groudiev@ens.psl.eu" }, { name = "Frederike Dümbgen", email = "frederike.duembgen@gmail.com" } ] -description = "Codebase for Kernel Sums of Squares (KernelSOS)" +description = "Implementation of the Kernel Sum-of-Squares optimization framework and various related solvers" readme = "README.md" license = { file = "LICENSE" } -urls = { Homepage = "https://github.com/agroudiev/ksos_tools.git" } +urls = { Homepage = "https://github.com/Simple-Robotics/ksos-tools/" } [tool.setuptools.packages.find] exclude = ["tests*"] From 2cb135e8d023d04b0ed10f08ccc2dcfea56bb183 Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 2 Jan 2026 17:17:14 +0100 Subject: [PATCH 06/14] fix(ksos): numpy array conversion --- ksos_tools/solvers/ksos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksos_tools/solvers/ksos.py b/ksos_tools/solvers/ksos.py index 83b0be5..a80097c 100644 --- a/ksos_tools/solvers/ksos.py +++ b/ksos_tools/solvers/ksos.py @@ -397,7 +397,7 @@ def get_surrogate( for i, (fi_interp, fi) in enumerate(zip(values_samples, f_samples_min_c)): if abs((fi_interp - fi) / fi) > 1e-2: - msg = f"Warning: at sample {i}, surrogate function not passing directly through f: {float(fi_interp):.4f}, {float(fi):.4f}" + msg = f"Warning: at sample {i}, surrogate function not passing directly through f: {float(fi_interp.item()):.4f}, {float(fi.item()):.4f}" if errors == "print": print(msg) elif errors == "raise": From 04433cd1d05942a863ca90cf582455f18e6e04e9 Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 2 Jan 2026 17:27:59 +0100 Subject: [PATCH 07/14] refactor: fix flake8 lints --- ksos_tools/examples/polynomial.py | 4 ---- ksos_tools/solvers/helpers.py | 3 +-- ksos_tools/solvers/ksos.py | 7 ++++++- ksos_tools/solvers/newton.py | 10 +++------- ksos_tools/solvers/problem.py | 2 +- ksos_tools/solvers/sos.py | 2 +- setup.cfg | 2 +- tests/test_benchmarks.py | 2 +- 8 files changed, 14 insertions(+), 18 deletions(-) diff --git a/ksos_tools/examples/polynomial.py b/ksos_tools/examples/polynomial.py index e73dc8b..8de7b23 100644 --- a/ksos_tools/examples/polynomial.py +++ b/ksos_tools/examples/polynomial.py @@ -1,5 +1,3 @@ -import warnings - import matplotlib.pylab as plt import numpy as np @@ -36,8 +34,6 @@ def __repr__(self): if __name__ == "__main__": - import matplotlib.pylab as plt - poly = Polynomial() poly.plot() plt.show(block=False) diff --git a/ksos_tools/solvers/helpers.py b/ksos_tools/solvers/helpers.py index 9c8e220..e8f36dc 100644 --- a/ksos_tools/solvers/helpers.py +++ b/ksos_tools/solvers/helpers.py @@ -1,7 +1,6 @@ import cvxpy as cp -import numpy as np -from ksos_tools.utils import duplication_matrix, hvec +from ksos_tools.utils import hvec def find_feasible_B(alpha, c, problem, soft=False): diff --git a/ksos_tools/solvers/ksos.py b/ksos_tools/solvers/ksos.py index a80097c..07c36e7 100644 --- a/ksos_tools/solvers/ksos.py +++ b/ksos_tools/solvers/ksos.py @@ -102,7 +102,12 @@ def solve( radius = np.array([radius] * dim) assert n_samples is None or n_samples >= 1 assert lambd >= 0 - assert (kernel == "Periodic" and isinstance(sigma, tuple) and sigma[0] > 0 and sigma[1] > 0) or sigma > 0 + assert ( + kernel == "Periodic" + and isinstance(sigma, tuple) + and sigma[0] > 0 + and sigma[1] > 0 + ) or sigma > 0 assert warm_iterations >= 1 if samples is not None: assert np.ndim(samples) >= 2 diff --git a/ksos_tools/solvers/newton.py b/ksos_tools/solvers/newton.py index 29ffef9..032cb95 100644 --- a/ksos_tools/solvers/newton.py +++ b/ksos_tools/solvers/newton.py @@ -1,19 +1,15 @@ import warnings -import eigenpy import numpy as np import scipy import scipy.linalg +from ksos_tools.solvers.problem import Problem DEBUG = False TOL_EIG_MIN = -1e-8 # values below this are considered negative. -from ksos_tools.solvers.helpers import find_feasible_B -from ksos_tools.solvers.problem import Problem - - def armijo_linesearch(cost_before, alpha, stepsize, delta, grad_H, problem: Problem): beta = 0.25 gamma = 1e-4 @@ -175,7 +171,7 @@ def solve_newton_system(alpha): # solve = lambda x: llt.solve(x) a, b = scipy.linalg.cho_factor(H_pp) solve = lambda x: scipy.linalg.cho_solve((a, b), x) - except: + except scipy.linalg.LinAlgError: warnings.warn("Hessian is not positive definite?") a, b = scipy.linalg.lu_factor(H_pp) solve = lambda x: scipy.linalg.lu_solve((a, b), x) @@ -302,7 +298,7 @@ def solve_newton_system(alpha, grad_H, hess_H): solve = lambda x: scipy.linalg.cho_solve((a, b), x) # llt = eigenpy.LLT(hess_H) # solve = lambda x: llt.solve(x) - except: + except scipy.linalg.LinAlgError: a, b = scipy.linalg.lu_factor(hess_H) solve = lambda x: scipy.linalg.lu_solve((a, b), x) g1 = solve(grad_H).flatten() diff --git a/ksos_tools/solvers/problem.py b/ksos_tools/solvers/problem.py index 57f7f8c..054528e 100644 --- a/ksos_tools/solvers/problem.py +++ b/ksos_tools/solvers/problem.py @@ -66,7 +66,7 @@ def decompose(K, method): elif method == "numpy": try: R = np.linalg.cholesky(K).T - except: + except np.linalg.LinAlgError: raise ValueError("Kernel matrix not positive!") elif method == "eigenpy": llt = eigenpy.LLT(K) diff --git a/ksos_tools/solvers/sos.py b/ksos_tools/solvers/sos.py index 27c5f61..37510c4 100644 --- a/ksos_tools/solvers/sos.py +++ b/ksos_tools/solvers/sos.py @@ -100,7 +100,7 @@ def solve_from_samples( return None, {"cost": None, "ttot": ttot} else: ttot = time.time() - t1 - if not "optimal" in prob.status: + if "optimal" not in prob.status: print("No solution found:", prob.status) return None, {"cost": None, "ttot": ttot} diff --git a/setup.cfg b/setup.cfg index 412bc27..efdca0a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,4 @@ [flake8] -ignore = W292, W391, F541, F841, E203, E501, W503, E741, E712 +ignore = W292, W391, F541, F841, E203, E501, W503, E741, E712, E731 exclude = _notebooks/*, *.ipynb_checkpoints*, venv/ max-line-length = 99 diff --git a/tests/test_benchmarks.py b/tests/test_benchmarks.py index 1df11ed..f5c50fb 100644 --- a/tests/test_benchmarks.py +++ b/tests/test_benchmarks.py @@ -42,7 +42,7 @@ def plot_solutions(center, radius, info, x_gt, f): ) @pytest.mark.parametrize("kernel", ("Laplace", "Gauss")) def test_ackley(solver, kernel, plot=False): - f_here = lambda x: ackley(x) # type: ignore + f_here = lambda x: ackley(x) # type: ignore # noqa: E731 dim = 2 x_gt = np.zeros(dim) From e73a8dae948d1f8dd3b5599ba3108c10c4b0da50 Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 2 Jan 2026 17:37:17 +0100 Subject: [PATCH 08/14] feat(CI): add new platforms to CI for testing --- .github/workflows/python-package-conda.yml | 105 ++++++++++++++++++++- 1 file changed, 100 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index 0cb01d6..7795790 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -1,13 +1,10 @@ name: Python Package using Conda -on: [push] +on: [push, pull_request] jobs: - build-linux: + lint: runs-on: ubuntu-latest - strategy: - max-parallel: 5 - steps: - uses: actions/checkout@v4 - name: Set up Python 3.12 @@ -34,3 +31,101 @@ jobs: run: | conda install pytest pytest + + linux: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: ubuntu-22.04 + target: x86_64 + - runner: ubuntu-22.04 + target: x86 + - runner: ubuntu-22.04 + target: aarch64 + - runner: ubuntu-22.04 + target: armv7 + - runner: ubuntu-22.04 + target: s390x + - runner: ubuntu-22.04 + target: ppc64le + + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.12 + uses: actions/setup-python@v3 + with: + python-version: '3.12' + - name: Add conda to system path + run: | + # $CONDA is an environment variable pointing to the root of the miniconda directory + echo $CONDA/bin >> $GITHUB_PATH + - name: Install dependencies + run: | + conda env update --file ksos_env.yml --name base + - name: Test with pytest + env: + MOSEKLM_LICENSE_FILE: ${{ secrets.MOSEK_LICENSE }} + run: | + conda install pytest + pytest + + windows: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: windows-latest + target: x64 + - runner: windows-latest + target: x86 + + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.12 + uses: actions/setup-python@v3 + with: + python-version: '3.12' + - name: Add conda to system path + run: | + # $CONDA is an environment variable pointing to the root of the miniconda directory + echo $CONDA/bin >> $GITHUB_PATH + - name: Install dependencies + run: | + conda env update --file ksos_env.yml --name base + - name: Test with pytest + env: + MOSEKLM_LICENSE_FILE: ${{ secrets.MOSEK_LICENSE }} + run: | + conda install pytest + pytest + + macos: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: macos-14 + target: x86_64 + - runner: macos-14 + target: aarch64 + + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.12 + uses: actions/setup-python@v3 + with: + python-version: '3.12' + - name: Add conda to system path + run: | + # $CONDA is an environment variable pointing to the root of the miniconda directory + echo $CONDA/bin >> $GITHUB_PATH + - name: Install dependencies + run: | + conda env update --file ksos_env.yml --name base + - name: Test with pytest + env: + MOSEKLM_LICENSE_FILE: ${{ secrets.MOSEK_LICENSE }} + run: | + conda install pytest + pytest From bbee254399497e9ad65e036af7556c6dcf2ea827 Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 2 Jan 2026 17:59:16 +0100 Subject: [PATCH 09/14] test(polynomial): disabled simple newton for polynomial kernel --- tests/test_polynomial_problems.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_polynomial_problems.py b/tests/test_polynomial_problems.py index b32c453..2c0b487 100644 --- a/tests/test_polynomial_problems.py +++ b/tests/test_polynomial_problems.py @@ -207,7 +207,7 @@ def test_polynomial_kernel(): if soft_constraints: solvers = ["MOSEK"] else: - solvers = ["MOSEK", "newton", "newton-kernel", "newton-features"] + solvers = ["MOSEK", "newton-kernel", "newton-features"] # disable newton for solver in solvers: print( From d35be017f6fa8d67c0b9cb8ce80b07821db103aa Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 2 Jan 2026 18:04:11 +0100 Subject: [PATCH 10/14] chore(CHANGELOG): add changelog --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b8ae2dd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog + +This file is used to track changes made to the project over time. + +## [Unreleased] +### Additions +- Support for Sobol sequences in polynomial problem sampling +- Support for periodic kernel + +## [0.2.2] - 2025-11-14 +### Additions +- Add penalty parameter for soft-constrained version used with polynomial kernels +- Create helper functions for monomial feature function creation for polynomial kernel + +### Improvements +- Improve timing calculations +- Improve efficiency of surrogate function calculation \ No newline at end of file From 14cfdd6f7cf357b18d20e8c8fd454df96b287f8a Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 2 Jan 2026 18:08:27 +0100 Subject: [PATCH 11/14] fix(CI): install miniconda for windows and macos --- .github/workflows/python-package-conda.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index 7795790..6fee11a 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -86,10 +86,11 @@ jobs: uses: actions/setup-python@v3 with: python-version: '3.12' - - name: Add conda to system path - run: | - # $CONDA is an environment variable pointing to the root of the miniconda directory - echo $CONDA/bin >> $GITHUB_PATH + architecture: ${{ matrix.platform.target }} + - uses: conda-incubator/setup-miniconda@v3 + with: + auto-update-conda: true + python-version: '3.12' - name: Install dependencies run: | conda env update --file ksos_env.yml --name base @@ -116,10 +117,10 @@ jobs: uses: actions/setup-python@v3 with: python-version: '3.12' - - name: Add conda to system path - run: | - # $CONDA is an environment variable pointing to the root of the miniconda directory - echo $CONDA/bin >> $GITHUB_PATH + - uses: conda-incubator/setup-miniconda@v3 + with: + auto-update-conda: true + python-version: '3.12' - name: Install dependencies run: | conda env update --file ksos_env.yml --name base From c75ff9935c25cbc3612557ae832a3cc334261ffd Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 2 Jan 2026 18:15:49 +0100 Subject: [PATCH 12/14] fix(CI): run pytest from conda --- .github/workflows/python-package-conda.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index 6fee11a..c330299 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -99,7 +99,7 @@ jobs: MOSEKLM_LICENSE_FILE: ${{ secrets.MOSEK_LICENSE }} run: | conda install pytest - pytest + python -m pytest macos: runs-on: ${{ matrix.platform.runner }} @@ -129,4 +129,4 @@ jobs: MOSEKLM_LICENSE_FILE: ${{ secrets.MOSEK_LICENSE }} run: | conda install pytest - pytest + python -m pytest From 9d0bec085304966702d6c99ffcbc2f206919150f Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 2 Jan 2026 18:32:10 +0100 Subject: [PATCH 13/14] fix(CI): run tests in ksos_env --- .github/workflows/python-package-conda.yml | 12 ++++++------ ksos_env.yml | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index c330299..4254f75 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -62,12 +62,12 @@ jobs: echo $CONDA/bin >> $GITHUB_PATH - name: Install dependencies run: | - conda env update --file ksos_env.yml --name base + conda env create --file ksos_env.yml --name ksos_env - name: Test with pytest env: MOSEKLM_LICENSE_FILE: ${{ secrets.MOSEK_LICENSE }} run: | - conda install pytest + conda activate ksos_env pytest windows: @@ -93,12 +93,12 @@ jobs: python-version: '3.12' - name: Install dependencies run: | - conda env update --file ksos_env.yml --name base + conda env create --file ksos_env.yml --name ksos_env - name: Test with pytest env: MOSEKLM_LICENSE_FILE: ${{ secrets.MOSEK_LICENSE }} run: | - conda install pytest + conda activate ksos_env python -m pytest macos: @@ -123,10 +123,10 @@ jobs: python-version: '3.12' - name: Install dependencies run: | - conda env update --file ksos_env.yml --name base + conda env create --file ksos_env.yml --name ksos_env - name: Test with pytest env: MOSEKLM_LICENSE_FILE: ${{ secrets.MOSEK_LICENSE }} run: | - conda install pytest + conda activate ksos_env python -m pytest diff --git a/ksos_env.yml b/ksos_env.yml index 63c3dfe..7197543 100644 --- a/ksos_env.yml +++ b/ksos_env.yml @@ -12,6 +12,7 @@ dependencies: - matplotlib - numpy - scipy + - pytest - pip: - mosek - -e . From 6ab37db1b5f870b5bf5f7c88c6c1723a1762afc1 Mon Sep 17 00:00:00 2001 From: Antoine Groudiev Date: Fri, 2 Jan 2026 18:46:59 +0100 Subject: [PATCH 14/14] fix(CI): source conda shell --- .github/workflows/python-package-conda.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml index 4254f75..82d5851 100644 --- a/.github/workflows/python-package-conda.yml +++ b/.github/workflows/python-package-conda.yml @@ -67,6 +67,7 @@ jobs: env: MOSEKLM_LICENSE_FILE: ${{ secrets.MOSEK_LICENSE }} run: | + source $CONDA/etc/profile.d/conda.sh conda activate ksos_env pytest @@ -128,5 +129,6 @@ jobs: env: MOSEKLM_LICENSE_FILE: ${{ secrets.MOSEK_LICENSE }} run: | + source $CONDA/etc/profile.d/conda.sh conda activate ksos_env python -m pytest