Skip to content

oberbichler/cslsqp

Repository files navigation

cslsqp logo

cslsqp

A fast, pure-C# reimplementation of the classic SLSQP (Sequential Least-Squares Quadratic Programming) optimiser — written in 100% managed .NET 10 — featuring high-performance Span-based APIs and zero native/unmanaged dependencies.

cslsqp delivers the robust non-linear constrained optimization of SLSQP directly to C# developers, eliminating native library marshalling and deployment baggage.

✨ Features

  • 🚀 100% Pure C# solver core — the entire iteration loop, BFGS update, QP sub-problem, and line-search run in fully compiled, managed .NET 10.
  • 🪶 Zero native dependencies — pure C# with no need for Fortran, C/C++ libraries, or external native binary DLLs.
  • Zero-allocation — leverages ReadOnlySpan<double> and Span<double> delegates to avoid heap allocations in critical function and gradient evaluation loops.
  • 🧮 SIMD Acceleration — utilizes System.Numerics.Tensors for hardware-accelerated vector and matrix operations.
  • 🔌 Simple ease-of-use — a standard static SlsqpSolver.Optimize method accepts traditional array-based delegates or high-performance Span-based delegates.
  • 🛠 Flexible configuration — supports analytical gradients or finite differences (Forward, Backward, Central), inexact or exact line-search, and NNLS/BVLS sub-solvers.

Installation

Add the package via NuGet:

dotnet add package cslsqp

Runtime requirement: .NET 10.0+.

Quick start

For quick and easy setup, use standard array-based delegates:

using System;
using cslsqp;
using cslsqp.Types;

// Objective function: Rosenbrock function
Func<double[], double> objective = (x) =>
    100.0 * Math.Pow(x[1] - x[0] * x[0], 2) + Math.Pow(1.0 - x[0], 2);

// Constraint: c(x) >= 0 (inequality)
Func<double[], double[]> constraints = (x) =>
    new double[] { 1.0 - x[0] * x[0] - x[1] * x[1] };

double[] x0 = { 0.1, 0.1 };
double[] xl = { -1.0, -1.0 }; // lower bounds
double[] xu = { 1.0, 1.0 };  // upper bounds

SlsqpResult result = SlsqpSolver.Optimize(
    objective,
    x0,
    constraints: constraints,
    xl: xl,
    xu: xu,
    gradientMode: GradientMode.Central
);

Console.WriteLine($"Status: {result.Status} (Success: {result.Success})");
Console.WriteLine($"Function value: {result.Fun}");
Console.WriteLine($"Solution: [{string.Join(", ", result.X)}]");

High-performance Span-based interface

To avoid heap allocations in inner loops, use the high-performance Span-based interface:

using System;
using cslsqp;
using cslsqp.Types;

// Zero-allocation objective
Func<ReadOnlySpan<double>, double> objective = (x) =>
    100.0 * Math.Pow(x[1] - x[0] * x[0], 2) + Math.Pow(1.0 - x[0], 2);

// Zero-allocation constraints (write directly into the output Span)
Action<ReadOnlySpan<double>, Span<double>> constraints = (x, c) =>
{
    c[0] = 1.0 - x[0] * x[0] - x[1] * x[1]; // inequality: c[0] >= 0
};

double[] x0 = { 0.1, 0.1 };
double[] xl = { -1.0, -1.0 };
double[] xu = { 1.0, 1.0 };

SlsqpResult result = SlsqpSolver.Optimize(
    objective,
    x0,
    m: 1, // specify total number of constraints
    constraints: constraints,
    xl: xl,
    xu: xu,
    gradientMode: GradientMode.Central
);

Console.WriteLine($"Status: {result.Status} (Success: {result.Success})");
Console.WriteLine($"Function value: {result.Fun}");

API overview

Enums

Enum Values Description
GradientMode User, Backward, Forward, Central How gradients are supplied or approximated
LinesearchMode Inexact, Exact Line-search strategy
NnlsMode Nnls, Bvls Non-negative least-squares sub-solver
SlsqpStatus Converged, MaxIterationsReached, FuncEvalRequired, … Solver exit status

Classes / Results

Class Description
SlsqpSolver High-level static entry point — configure and run Optimize(...)
SlsqpResult Output containing X (solution), Fun (minimum value), Constraints (final constraint values), Status, Message, Iterations, Success, NFev, and NJev

Benchmark

Below are the benchmark results from an Apple M1 Pro (10 runs, 2 warm-ups, matching rslsqp benchmarks):

Problem n Constraints Wall-Clock (ms) nit nfev njev Success
Rosenbrock unconstrained 50 0 38.09 229 626 229
Rosenbrock unconstrained 100 0 373.57 451 1236 451
Rosenbrock unconstrained 200 0 2955.57 501 1583 501
Rosenbrock constrained 50 2 13.32 85 312 85
Rosenbrock constrained 100 2 162.62 171 701 171
Portfolio optimisation 50 2 8.78 47 88 47
Portfolio optimisation 100 2 63.48 51 99 51
Portfolio optimisation 200 2 280.48 33 62 33
Quadratic + 100 ineq 50 100 26.86 38 114 34
Quadratic + 200 ineq 100 200 361.64 52 210 48
Least-squares fitting 20 1 0.43 5 13 5
Least-squares fitting 40 1 0.34 2 2 2

To run the direct benchmarks on your machine:

dotnet run --project cslsqp.Benchmarks -c Release

Licence

ISC — see LICENSE for details.

Based on the SLSQP algorithm by Dieter Kraft (1988), modernised in Fortran by Jacob Williams (slsqp, BSD-3-Clause).

About

A fast, pure C# reimplementation of the SLSQP (Sequential Least-Squares Quadratic Programming) non-linear constrained optimization solver for .NET.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages