qoco 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qoco-0.1.0/.github/workflows/build.yml +58 -0
- qoco-0.1.0/.github/workflows/unit_tests.yml +51 -0
- qoco-0.1.0/.gitignore +5 -0
- qoco-0.1.0/CMakeLists.txt +33 -0
- qoco-0.1.0/LICENSE +28 -0
- qoco-0.1.0/PKG-INFO +47 -0
- qoco-0.1.0/README.md +32 -0
- qoco-0.1.0/pyproject.toml +20 -0
- qoco-0.1.0/requirements.txt +7 -0
- qoco-0.1.0/src/bindings.cpp +313 -0
- qoco-0.1.0/src/qoco/__init__.py +1 -0
- qoco-0.1.0/src/qoco/codegen.py +1834 -0
- qoco-0.1.0/src/qoco/codegen_utils.py +120 -0
- qoco-0.1.0/src/qoco/interface.py +172 -0
- qoco-0.1.0/tests/__init__.py +0 -0
- qoco-0.1.0/tests/test_lcvx.py +107 -0
- qoco-0.1.0/tests/test_lcvx_bad_scale.py +111 -0
- qoco-0.1.0/tests/test_linear_objective.py +41 -0
- qoco-0.1.0/tests/test_no_constraints.py +44 -0
- qoco-0.1.0/tests/test_no_eq_constraints.py +45 -0
- qoco-0.1.0/tests/test_no_ineq_constraints.py +44 -0
- qoco-0.1.0/tests/test_no_soc_constraints.py +45 -0
- qoco-0.1.0/tests/test_pdg.py +133 -0
- qoco-0.1.0/tests/test_robust_kalman_filter.py +94 -0
- qoco-0.1.0/tests/test_simple_socp1.py +45 -0
- qoco-0.1.0/tests/test_simple_socp2.py +45 -0
- qoco-0.1.0/tests/test_simple_socp3.py +45 -0
- qoco-0.1.0/tests/utils/__init__.py +0 -0
- qoco-0.1.0/tests/utils/cvxpy_to_qoco.py +27 -0
- qoco-0.1.0/tests/utils/run_generated_solver.py +20 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Taken from osqp-python
|
|
2
|
+
|
|
3
|
+
name: Build Wheels
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
# Triggers the workflow on push or pull request events
|
|
7
|
+
push:
|
|
8
|
+
branches:
|
|
9
|
+
- "*"
|
|
10
|
+
- "*/*"
|
|
11
|
+
- "**"
|
|
12
|
+
pull_request:
|
|
13
|
+
branches: [main]
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
build_sdist:
|
|
17
|
+
name: Build source
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
strategy:
|
|
20
|
+
fail-fast: false
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@master
|
|
23
|
+
with:
|
|
24
|
+
submodules: "recursive"
|
|
25
|
+
|
|
26
|
+
- name: Build source and wheel
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install build
|
|
29
|
+
python -m build --outdir=wheelhouse
|
|
30
|
+
|
|
31
|
+
- name: Upload sdist and wheel to github
|
|
32
|
+
uses: actions/upload-artifact@v4
|
|
33
|
+
with:
|
|
34
|
+
name: wheels-sdist
|
|
35
|
+
path: wheelhouse/*
|
|
36
|
+
if-no-files-found: error
|
|
37
|
+
|
|
38
|
+
build_wheels:
|
|
39
|
+
name: Building wheels on ${{ matrix.os }}
|
|
40
|
+
runs-on: ${{ matrix.os }}
|
|
41
|
+
strategy:
|
|
42
|
+
fail-fast: false
|
|
43
|
+
matrix:
|
|
44
|
+
os: [ubuntu-latest, macos-13, macos-latest]
|
|
45
|
+
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v4
|
|
48
|
+
with:
|
|
49
|
+
fetch-depth: 0
|
|
50
|
+
submodules: true
|
|
51
|
+
|
|
52
|
+
- uses: pypa/cibuildwheel@v2.21
|
|
53
|
+
|
|
54
|
+
- name: Upload wheels
|
|
55
|
+
uses: actions/upload-artifact@v4
|
|
56
|
+
with:
|
|
57
|
+
name: cibw-wheels-${{ matrix.os }}
|
|
58
|
+
path: wheelhouse/*.whl
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# This is a basic workflow to help you get started with Actions
|
|
2
|
+
|
|
3
|
+
name: Unit Tests
|
|
4
|
+
|
|
5
|
+
# Controls when the workflow will run
|
|
6
|
+
on:
|
|
7
|
+
# Triggers the workflow on push or pull request events
|
|
8
|
+
push:
|
|
9
|
+
branches:
|
|
10
|
+
- "*"
|
|
11
|
+
- "*/*"
|
|
12
|
+
- "**"
|
|
13
|
+
pull_request:
|
|
14
|
+
branches: [main]
|
|
15
|
+
|
|
16
|
+
# Allows you to run this workflow manually from the Actions tab
|
|
17
|
+
workflow_dispatch:
|
|
18
|
+
|
|
19
|
+
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
|
20
|
+
jobs:
|
|
21
|
+
# This workflow contains a single job called "build"
|
|
22
|
+
build:
|
|
23
|
+
# The type of runner that the job will run on
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
|
|
26
|
+
strategy:
|
|
27
|
+
# Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable.
|
|
28
|
+
fail-fast: false
|
|
29
|
+
|
|
30
|
+
matrix:
|
|
31
|
+
os: [ubuntu-latest]
|
|
32
|
+
|
|
33
|
+
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
34
|
+
steps:
|
|
35
|
+
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: "3.11"
|
|
40
|
+
|
|
41
|
+
- name: Install dependencies
|
|
42
|
+
run: >
|
|
43
|
+
pip install "pybind11[global]" pytest pytest-xdist cvxpy
|
|
44
|
+
|
|
45
|
+
- name: Install qoco
|
|
46
|
+
run: >
|
|
47
|
+
pip install .
|
|
48
|
+
|
|
49
|
+
- name: Run Pytest
|
|
50
|
+
run: >
|
|
51
|
+
pytest -n 12
|
qoco-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.15)
|
|
2
|
+
project(extension)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 11)
|
|
5
|
+
|
|
6
|
+
set(QOCO_BUILD_TYPE "Release")
|
|
7
|
+
|
|
8
|
+
# Detect operating system.
|
|
9
|
+
message(STATUS "We are on a ${CMAKE_SYSTEM_NAME} system")
|
|
10
|
+
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
|
11
|
+
add_compile_definitions(IS_LINUX)
|
|
12
|
+
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
|
13
|
+
add_compile_definitions(IS_MACOS)
|
|
14
|
+
endif()
|
|
15
|
+
|
|
16
|
+
find_package(pybind11 REQUIRED)
|
|
17
|
+
|
|
18
|
+
message(STATUS "Fetching/configuring QOCO")
|
|
19
|
+
list(APPEND CMAKE_MESSAGE_INDENT " ")
|
|
20
|
+
include(FetchContent)
|
|
21
|
+
FetchContent_Declare(
|
|
22
|
+
qoco
|
|
23
|
+
GIT_REPOSITORY https://github.com/qoco-org/qoco.git
|
|
24
|
+
GIT_TAG 5abc44574b62e5a28cdae8e97ea70ecacf1f954c
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
list(POP_BACK CMAKE_MESSAGE_INDENT)
|
|
28
|
+
FetchContent_MakeAvailable(qoco)
|
|
29
|
+
|
|
30
|
+
pybind11_add_module(qoco_ext src/bindings.cpp)
|
|
31
|
+
target_include_directories(qoco_ext INTERFACE ${qoco_SOURCE_DIR}/include)
|
|
32
|
+
target_link_libraries(qoco_ext PUBLIC pybind11::module qocostatic)
|
|
33
|
+
install(TARGETS qoco_ext DESTINATION . COMPONENT python)
|
qoco-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, Govind Chari
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
qoco-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: qoco
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: QOCO: Quadratic Objective Conic Optimizer
|
|
5
|
+
Author-Email: "Govind M. Chari" <govindchari1@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/qoco-org/qoco
|
|
7
|
+
Project-URL: Issues, https://github.com/qoco-org/qoco/issues
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Requires-Dist: jinja2
|
|
10
|
+
Requires-Dist: numpy>=1.7
|
|
11
|
+
Requires-Dist: qdldl
|
|
12
|
+
Requires-Dist: scipy>=0.13.2
|
|
13
|
+
Requires-Dist: setuptools
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# QOCO Python
|
|
17
|
+
<p align="center">
|
|
18
|
+
<img src="https://github.com/user-attachments/assets/7bd44fa7-d198-4739-bb79-a5c15e04a8de" alt="drawing" width="500"/>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<p align="center">
|
|
22
|
+
<a href="https://opensource.org/licenses/BSD-3-Clause"><img src="https://img.shields.io/badge/License-BSD_3--Clause-blue.svg" alt="License" /></a>
|
|
23
|
+
<a href=https://github.com/qoco-org/qoco-python/actions/workflows/unit_tests.yml/badge.svg"><img src="https://github.com/qoco-org/qoco-python/actions/workflows/unit_tests.yml/badge.svg"/></a>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
This repository contains the python wrapper for [QOCO](https://github.com/qoco-org/qoco) and the code generator QOCOGEN.
|
|
27
|
+
|
|
28
|
+
QOCOGEN is a code generator which takes in an second-order cone program problem family and generates a customized C solver (called qoco_custom) for the specified problem family which implements the same algorithm as QOCO. This customized solver is library-free, only uses static memory allocation, and can be a few times faster than QOCO.
|
|
29
|
+
|
|
30
|
+
## Bug reports
|
|
31
|
+
|
|
32
|
+
File any issues or bug reports using the [issue tracker](https://github.com/qoco-org/qoco-python/issues).
|
|
33
|
+
|
|
34
|
+
## Citing
|
|
35
|
+
```
|
|
36
|
+
@misc{chari2025qoco,
|
|
37
|
+
author = {Chari, Govind M. and Acikmese, Behcet},
|
|
38
|
+
title = {QOCO},
|
|
39
|
+
year = {2025},
|
|
40
|
+
publisher = {GitHub},
|
|
41
|
+
journal = {GitHub repository},
|
|
42
|
+
howpublished = {\url{https://github.com/qoco-org/qoco}},
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
QOCO is licensed under the BSD-3-Clause license.
|
qoco-0.1.0/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# QOCO Python
|
|
2
|
+
<p align="center">
|
|
3
|
+
<img src="https://github.com/user-attachments/assets/7bd44fa7-d198-4739-bb79-a5c15e04a8de" alt="drawing" width="500"/>
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="https://opensource.org/licenses/BSD-3-Clause"><img src="https://img.shields.io/badge/License-BSD_3--Clause-blue.svg" alt="License" /></a>
|
|
8
|
+
<a href=https://github.com/qoco-org/qoco-python/actions/workflows/unit_tests.yml/badge.svg"><img src="https://github.com/qoco-org/qoco-python/actions/workflows/unit_tests.yml/badge.svg"/></a>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
This repository contains the python wrapper for [QOCO](https://github.com/qoco-org/qoco) and the code generator QOCOGEN.
|
|
12
|
+
|
|
13
|
+
QOCOGEN is a code generator which takes in an second-order cone program problem family and generates a customized C solver (called qoco_custom) for the specified problem family which implements the same algorithm as QOCO. This customized solver is library-free, only uses static memory allocation, and can be a few times faster than QOCO.
|
|
14
|
+
|
|
15
|
+
## Bug reports
|
|
16
|
+
|
|
17
|
+
File any issues or bug reports using the [issue tracker](https://github.com/qoco-org/qoco-python/issues).
|
|
18
|
+
|
|
19
|
+
## Citing
|
|
20
|
+
```
|
|
21
|
+
@misc{chari2025qoco,
|
|
22
|
+
author = {Chari, Govind M. and Acikmese, Behcet},
|
|
23
|
+
title = {QOCO},
|
|
24
|
+
year = {2025},
|
|
25
|
+
publisher = {GitHub},
|
|
26
|
+
journal = {GitHub repository},
|
|
27
|
+
howpublished = {\url{https://github.com/qoco-org/qoco}},
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## License
|
|
32
|
+
QOCO is licensed under the BSD-3-Clause license.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["scikit-build-core", "pybind11"]
|
|
3
|
+
build-backend = "scikit_build_core.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "qoco"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "QOCO: Quadratic Objective Conic Optimizer"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
authors = [{ name = "Govind M. Chari", email = "govindchari1@gmail.com" }]
|
|
12
|
+
dependencies = ["jinja2", "numpy>=1.7", "qdldl", "scipy>=0.13.2", "setuptools"]
|
|
13
|
+
|
|
14
|
+
[tool.scikit-build]
|
|
15
|
+
install.components = ["python"]
|
|
16
|
+
wheel.install-dir = "qoco"
|
|
17
|
+
|
|
18
|
+
[project.urls]
|
|
19
|
+
Homepage = "https://github.com/qoco-org/qoco"
|
|
20
|
+
Issues = "https://github.com/qoco-org/qoco/issues"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
pybind11
|
|
2
|
+
numpy>=1.7
|
|
3
|
+
# Exclude scipy 1.12 because the random sparse array function started returning
|
|
4
|
+
# the transpose of the original, breaking the unit tests. This was fixed in 1.13.0.
|
|
5
|
+
# ref: https://github.com/scipy/scipy/issues/20027
|
|
6
|
+
scipy>=0.13.2,!=1.12.0
|
|
7
|
+
qdldl
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/* Copyright (c) 2024, Govind M. Chari <govindchari1@gmail.com> */
|
|
2
|
+
/* This source code is licensed under the BSD 3-Clause License */
|
|
3
|
+
|
|
4
|
+
#include <pybind11/pybind11.h>
|
|
5
|
+
#include <pybind11/numpy.h>
|
|
6
|
+
#include "qoco.h"
|
|
7
|
+
|
|
8
|
+
namespace py = pybind11;
|
|
9
|
+
using namespace pybind11::literals;
|
|
10
|
+
|
|
11
|
+
class CSC
|
|
12
|
+
{
|
|
13
|
+
public:
|
|
14
|
+
CSC(py::object A);
|
|
15
|
+
~CSC();
|
|
16
|
+
QOCOCscMatrix *getcsc() const;
|
|
17
|
+
py::array_t<QOCOFloat> _x;
|
|
18
|
+
py::array_t<QOCOInt> _i;
|
|
19
|
+
py::array_t<QOCOInt> _p;
|
|
20
|
+
QOCOInt m;
|
|
21
|
+
QOCOInt n;
|
|
22
|
+
QOCOInt nnz;
|
|
23
|
+
|
|
24
|
+
private:
|
|
25
|
+
QOCOCscMatrix *_csc;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
CSC::CSC(py::object A)
|
|
29
|
+
{
|
|
30
|
+
py::object spa = py::module::import("scipy.sparse");
|
|
31
|
+
|
|
32
|
+
if (A == py::none())
|
|
33
|
+
{
|
|
34
|
+
this->m = 0;
|
|
35
|
+
this->n = 0;
|
|
36
|
+
this->nnz = 0;
|
|
37
|
+
this->_csc = nullptr;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
else
|
|
41
|
+
{
|
|
42
|
+
py::tuple dim = A.attr("shape");
|
|
43
|
+
|
|
44
|
+
int m = dim[0].cast<int>();
|
|
45
|
+
int n = dim[1].cast<int>();
|
|
46
|
+
|
|
47
|
+
if (!spa.attr("isspmatrix_csc")(A))
|
|
48
|
+
{
|
|
49
|
+
A = spa.attr("csc_matrix")(A);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this->_p = A.attr("indptr").cast<py::array_t<QOCOInt, py::array::c_style>>();
|
|
53
|
+
this->_i = A.attr("indices").cast<py::array_t<QOCOInt, py::array::c_style>>();
|
|
54
|
+
this->_x = A.attr("data").cast<py::array_t<QOCOFloat, py::array::c_style>>();
|
|
55
|
+
|
|
56
|
+
this->_csc = new QOCOCscMatrix();
|
|
57
|
+
this->_csc->m = m;
|
|
58
|
+
this->_csc->n = n;
|
|
59
|
+
this->_csc->x = (QOCOFloat *)this->_x.data();
|
|
60
|
+
this->_csc->i = (QOCOInt *)this->_i.data();
|
|
61
|
+
this->_csc->p = (QOCOInt *)this->_p.data();
|
|
62
|
+
this->_csc->nnz = A.attr("nnz").cast<int>();
|
|
63
|
+
|
|
64
|
+
this->m = this->_csc->m;
|
|
65
|
+
this->n = this->_csc->n;
|
|
66
|
+
this->nnz = this->_csc->nnz;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
QOCOCscMatrix *CSC::getcsc() const
|
|
71
|
+
{
|
|
72
|
+
return this->_csc;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
CSC::~CSC()
|
|
76
|
+
{
|
|
77
|
+
if (this->_csc)
|
|
78
|
+
delete this->_csc;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
class PyQOCOSolution
|
|
82
|
+
{
|
|
83
|
+
public:
|
|
84
|
+
PyQOCOSolution(QOCOSolution &, QOCOInt, QOCOInt, QOCOInt);
|
|
85
|
+
py::array_t<QOCOFloat> get_x();
|
|
86
|
+
py::array_t<QOCOFloat> get_s();
|
|
87
|
+
py::array_t<QOCOFloat> get_y();
|
|
88
|
+
py::array_t<QOCOFloat> get_z();
|
|
89
|
+
QOCOSolution &_solution;
|
|
90
|
+
|
|
91
|
+
private:
|
|
92
|
+
QOCOInt _n;
|
|
93
|
+
QOCOInt _m;
|
|
94
|
+
QOCOInt _p;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
PyQOCOSolution::PyQOCOSolution(QOCOSolution &solution, QOCOInt n, QOCOInt m, QOCOInt p) : _n(n), _m(m), _p(p), _solution(solution)
|
|
98
|
+
{
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
py::array_t<QOCOFloat> PyQOCOSolution::get_x()
|
|
102
|
+
{
|
|
103
|
+
return py::array_t<QOCOFloat>(
|
|
104
|
+
{this->_n},
|
|
105
|
+
{sizeof(QOCOFloat)},
|
|
106
|
+
this->_solution.x);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
py::array_t<QOCOFloat> PyQOCOSolution::get_s()
|
|
110
|
+
{
|
|
111
|
+
return py::array_t<QOCOFloat>(
|
|
112
|
+
{this->_m},
|
|
113
|
+
{sizeof(QOCOFloat)},
|
|
114
|
+
this->_solution.s);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
py::array_t<QOCOFloat> PyQOCOSolution::get_y()
|
|
118
|
+
{
|
|
119
|
+
return py::array_t<QOCOFloat>(
|
|
120
|
+
{this->_p},
|
|
121
|
+
{sizeof(QOCOFloat)},
|
|
122
|
+
this->_solution.y);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
py::array_t<QOCOFloat> PyQOCOSolution::get_z()
|
|
126
|
+
{
|
|
127
|
+
return py::array_t<QOCOFloat>(
|
|
128
|
+
{this->_m},
|
|
129
|
+
{sizeof(QOCOFloat)},
|
|
130
|
+
this->_solution.z);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
class PyQOCOSolver
|
|
134
|
+
{
|
|
135
|
+
public:
|
|
136
|
+
PyQOCOSolver(QOCOInt, QOCOInt, QOCOInt, const CSC &, const py::array_t<QOCOFloat>, const CSC &, const py::array_t<QOCOFloat>, const CSC &, const py::array_t<QOCOFloat>, QOCOInt, QOCOInt, const py::array_t<QOCOInt>, QOCOSettings *);
|
|
137
|
+
~PyQOCOSolver();
|
|
138
|
+
|
|
139
|
+
QOCOSettings *get_settings();
|
|
140
|
+
PyQOCOSolution &get_solution();
|
|
141
|
+
|
|
142
|
+
QOCOInt update_settings(const QOCOSettings &);
|
|
143
|
+
// QOCOInt update_vector_data(py::object, py::object, py::object);
|
|
144
|
+
// QOCOInt update_matrix_data(py::object, py::object, py::object);
|
|
145
|
+
|
|
146
|
+
QOCOInt solve();
|
|
147
|
+
|
|
148
|
+
private:
|
|
149
|
+
QOCOInt n;
|
|
150
|
+
QOCOInt m;
|
|
151
|
+
QOCOInt p;
|
|
152
|
+
|
|
153
|
+
const CSC &_P;
|
|
154
|
+
py::array_t<QOCOFloat> _c;
|
|
155
|
+
|
|
156
|
+
const CSC &_A;
|
|
157
|
+
py::array_t<QOCOFloat> _b;
|
|
158
|
+
|
|
159
|
+
const CSC &_G;
|
|
160
|
+
py::array_t<QOCOFloat> _h;
|
|
161
|
+
|
|
162
|
+
QOCOInt l;
|
|
163
|
+
QOCOInt nsoc;
|
|
164
|
+
py::array_t<QOCOInt> _q;
|
|
165
|
+
|
|
166
|
+
QOCOSolver *_solver;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
PyQOCOSolver::PyQOCOSolver(QOCOInt n, QOCOInt m, QOCOInt p, const CSC &P, const py::array_t<QOCOFloat> c, const CSC &A, const py::array_t<QOCOFloat> b, const CSC &G, const py::array_t<QOCOFloat> h, QOCOInt l, QOCOInt nsoc, const py::array_t<QOCOInt> q, QOCOSettings *settings) : n(n), m(m), p(p), _P(P), _c(c), _A(A), _b(b), _G(G), _h(h), l(l), nsoc(nsoc), _q(q)
|
|
170
|
+
{
|
|
171
|
+
this->_solver = new QOCOSolver();
|
|
172
|
+
|
|
173
|
+
py::tuple dim = this->_b.attr("shape");
|
|
174
|
+
QOCOFloat *bptr;
|
|
175
|
+
if (dim[0].cast<int>() == 0)
|
|
176
|
+
{
|
|
177
|
+
bptr = (QOCOFloat *)nullptr;
|
|
178
|
+
}
|
|
179
|
+
else
|
|
180
|
+
{
|
|
181
|
+
bptr = (QOCOFloat *)this->_b.data();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
dim = this->_h.attr("shape");
|
|
185
|
+
QOCOFloat *hptr;
|
|
186
|
+
if (dim[0].cast<int>() == 0)
|
|
187
|
+
{
|
|
188
|
+
hptr = nullptr;
|
|
189
|
+
}
|
|
190
|
+
else
|
|
191
|
+
{
|
|
192
|
+
hptr = (QOCOFloat *)this->_h.data();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
dim = this->_q.attr("shape");
|
|
196
|
+
QOCOInt *qptr;
|
|
197
|
+
if (dim[0].cast<int>() == 0)
|
|
198
|
+
{
|
|
199
|
+
qptr = nullptr;
|
|
200
|
+
}
|
|
201
|
+
else
|
|
202
|
+
{
|
|
203
|
+
qptr = (QOCOInt *)this->_q.data();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
QOCOInt status = qoco_setup(this->_solver, n, m, p, this->_P.getcsc(), (QOCOFloat *)this->_c.data(), this->_A.getcsc(), bptr, this->_G.getcsc(), hptr, l, nsoc, qptr, settings);
|
|
207
|
+
|
|
208
|
+
if (status)
|
|
209
|
+
{
|
|
210
|
+
std::string message = "Setup Error (Error Code " + std::to_string(status) + ")";
|
|
211
|
+
throw py::value_error(message);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
PyQOCOSolver::~PyQOCOSolver()
|
|
216
|
+
{
|
|
217
|
+
qoco_cleanup(this->_solver);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
QOCOSettings *PyQOCOSolver::get_settings()
|
|
221
|
+
{
|
|
222
|
+
return this->_solver->settings;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
PyQOCOSolution &PyQOCOSolver::get_solution()
|
|
226
|
+
{
|
|
227
|
+
PyQOCOSolution *solution = new PyQOCOSolution(*this->_solver->sol, this->n, this->m, this->p);
|
|
228
|
+
return *solution;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
QOCOInt PyQOCOSolver::solve()
|
|
232
|
+
{
|
|
233
|
+
py::gil_scoped_release release;
|
|
234
|
+
QOCOInt status = qoco_solve(this->_solver);
|
|
235
|
+
py::gil_scoped_acquire acquire;
|
|
236
|
+
return status;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
QOCOInt PyQOCOSolver::update_settings(const QOCOSettings &new_settings)
|
|
240
|
+
{
|
|
241
|
+
return qoco_update_settings(this->_solver, &new_settings);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
PYBIND11_MODULE(qoco_ext, m)
|
|
245
|
+
{
|
|
246
|
+
// Enums.
|
|
247
|
+
py::enum_<qoco_solve_status>(m, "qoco_solve_status", py::module_local())
|
|
248
|
+
.value("QOCO_UNSOLVED", QOCO_UNSOLVED)
|
|
249
|
+
.value("QOCO_SOLVED", QOCO_SOLVED)
|
|
250
|
+
.value("QOCO_SOLVED_INACCURATE", QOCO_SOLVED_INACCURATE)
|
|
251
|
+
.value("QOCO_MAX_ITER", QOCO_MAX_ITER)
|
|
252
|
+
.value("QOCO_NUMERICAL_ERROR", QOCO_NUMERICAL_ERROR)
|
|
253
|
+
.export_values();
|
|
254
|
+
|
|
255
|
+
// CSC.
|
|
256
|
+
py::class_<CSC>(m, "CSC", py::module_local())
|
|
257
|
+
.def(py::init<py::object>())
|
|
258
|
+
.def_readonly("m", &CSC::m)
|
|
259
|
+
.def_readonly("n", &CSC::n)
|
|
260
|
+
.def_readonly("x", &CSC::_x)
|
|
261
|
+
.def_readonly("i", &CSC::_i)
|
|
262
|
+
.def_readonly("p", &CSC::_p)
|
|
263
|
+
.def_readonly("nnz", &CSC::nnz);
|
|
264
|
+
|
|
265
|
+
// Settings.
|
|
266
|
+
py::class_<QOCOSettings>(m, "QOCOSettings", py::module_local())
|
|
267
|
+
.def(py::init([]()
|
|
268
|
+
{ return new QOCOSettings(); }))
|
|
269
|
+
.def_readwrite("max_iters", &QOCOSettings::max_iters)
|
|
270
|
+
.def_readwrite("bisect_iters", &QOCOSettings::bisect_iters)
|
|
271
|
+
.def_readwrite("ruiz_iters", &QOCOSettings::ruiz_iters)
|
|
272
|
+
.def_readwrite("iter_ref_iters", &QOCOSettings::iter_ref_iters)
|
|
273
|
+
.def_readwrite("abstol", &QOCOSettings::abstol)
|
|
274
|
+
.def_readwrite("reltol", &QOCOSettings::reltol)
|
|
275
|
+
.def_readwrite("abstol_inacc", &QOCOSettings::abstol_inacc)
|
|
276
|
+
.def_readwrite("reltol_inacc", &QOCOSettings::reltol_inacc)
|
|
277
|
+
.def_readwrite("kkt_static_reg", &QOCOSettings::kkt_static_reg)
|
|
278
|
+
.def_readwrite("kkt_dynamic_reg", &QOCOSettings::kkt_dynamic_reg)
|
|
279
|
+
.def_readwrite("verbose", &QOCOSettings::verbose);
|
|
280
|
+
|
|
281
|
+
m.def("set_default_settings", &set_default_settings);
|
|
282
|
+
|
|
283
|
+
// Solution.
|
|
284
|
+
py::class_<PyQOCOSolution>(m, "QOCOSolution", py::module_local())
|
|
285
|
+
.def_property_readonly("x", &PyQOCOSolution::get_x)
|
|
286
|
+
.def_property_readonly("s", &PyQOCOSolution::get_s)
|
|
287
|
+
.def_property_readonly("y", &PyQOCOSolution::get_y)
|
|
288
|
+
.def_property_readonly("z", &PyQOCOSolution::get_z)
|
|
289
|
+
.def_property_readonly("iters", [](const PyQOCOSolution &sol)
|
|
290
|
+
{ return sol._solution.iters; })
|
|
291
|
+
.def_property_readonly("setup_time_sec", [](const PyQOCOSolution &sol)
|
|
292
|
+
{ return sol._solution.setup_time_sec; })
|
|
293
|
+
.def_property_readonly("solve_time_sec", [](const PyQOCOSolution &sol)
|
|
294
|
+
{ return sol._solution.solve_time_sec; })
|
|
295
|
+
.def_property_readonly("obj", [](const PyQOCOSolution &sol)
|
|
296
|
+
{ return sol._solution.obj; })
|
|
297
|
+
.def_property_readonly("pres", [](const PyQOCOSolution &sol)
|
|
298
|
+
{ return sol._solution.pres; })
|
|
299
|
+
.def_property_readonly("dres", [](const PyQOCOSolution &sol)
|
|
300
|
+
{ return sol._solution.dres; })
|
|
301
|
+
.def_property_readonly("gap", [](const PyQOCOSolution &sol)
|
|
302
|
+
{ return sol._solution.gap; })
|
|
303
|
+
.def_property_readonly("status", [](const PyQOCOSolution &sol)
|
|
304
|
+
{ return sol._solution.status; });
|
|
305
|
+
|
|
306
|
+
// Solver.
|
|
307
|
+
py::class_<PyQOCOSolver>(m, "QOCOSolver", py::module_local())
|
|
308
|
+
.def(py::init<QOCOInt, QOCOInt, QOCOInt, const CSC &, const py::array_t<QOCOFloat>, const CSC &, const py::array_t<QOCOFloat>, const CSC &, const py::array_t<QOCOFloat>, QOCOInt, QOCOInt, const py::array_t<QOCOInt>, QOCOSettings *>(), "n"_a, "m"_a, "p"_a, "P"_a, "c"_a.noconvert(), "A"_a, "b"_a.noconvert(), "G"_a, "h"_a.noconvert(), "l"_a, "nsoc"_a, "q"_a.noconvert(), "settings"_a)
|
|
309
|
+
.def_property_readonly("solution", &PyQOCOSolver::get_solution, py::return_value_policy::reference)
|
|
310
|
+
.def("update_settings", &PyQOCOSolver::update_settings)
|
|
311
|
+
.def("solve", &PyQOCOSolver::solve)
|
|
312
|
+
.def("get_settings", &PyQOCOSolver::get_settings, py::return_value_policy::reference);
|
|
313
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from qoco.interface import QOCO
|