-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for MOSEK, linear, conic and mip #4452
Open
ulfworsoe
wants to merge
11
commits into
google:main
Choose a base branch
from
ulfworsoe:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
2f63f65
Add MOSEK support for linear interface and math_opt
ulfworsoe f29855e
fix: mosek link isuse
96b7cc3
add support for quadratic terms in MOSEK
fde4a63
working on quadratic support for mosek
a470460
fix mosek primal/dual obj mixup
e978115
add example
ulfworsoe 1b3535c
fix: mosek issues inputting conic constraints
08904f6
apply clang format to mosek files
8e3f9db
Update copyright notices
ulfworsoe 16fc398
fix mosek/cmake config for windows
ulfworsoe 88c821a
Merge branch 'google:main' into main
ulfworsoe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
# Copyright 2010-2024 Google LLC | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
#[=======================================================================[.rst: | ||
FindMosek | ||
-------- | ||
|
||
This module determines the Mosek library of the system. | ||
|
||
IMPORTED Targets | ||
^^^^^^^^^^^^^^^^ | ||
|
||
This module defines :prop_tgt:`IMPORTED` target ``mosek::mosek``, if | ||
Mosek has been found. | ||
|
||
Result Variables | ||
^^^^^^^^^^^^^^^^ | ||
|
||
This module defines the following variables: | ||
|
||
:: | ||
|
||
MOSEK_FOUND - True if Mosek found. | ||
|
||
Hints | ||
^^^^^ | ||
|
||
A user may set ``MOSEK_PLATFORM_DIR`` to a Mosek installation platform | ||
directoru to tell this module where to look, or ``MOSEK_BASE`` to point | ||
to path where the ``mosek/`` directory is located. | ||
#]=======================================================================] | ||
set(MOSEK_FOUND FALSE) | ||
message(STATUS "Locating MOSEK") | ||
|
||
if(CMAKE_C_COMPILER_LOADED) | ||
include (CheckIncludeFile) | ||
include (CheckCSourceCompiles) | ||
elseif(CMAKE_CXX_COMPILER_LOADED) | ||
include (CheckIncludeFileCXX) | ||
include (CheckCXXSourceCompiles) | ||
else() | ||
message(FATAL_ERROR "FindMosek only works if either C or CXX language is enabled") | ||
endif() | ||
|
||
if(APPLE) | ||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)") | ||
SET(MOSEK_PLATFORM_NAME "osxaarch64") | ||
elseif() | ||
message(FATAL_ERROR "Mosek not supported for ${CMAKE_SYSTEM} / ${CMAKE_SYSTEM_PROCESSOR}") | ||
endif() | ||
elseif(UNIX) | ||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)") | ||
SET(MOSEK_PLATFORM_NAME "linuxaarch64") | ||
else() | ||
SET(MOSEK_PLATFORM_NAME "linux64x86") | ||
endif() | ||
elseif(MSVC) | ||
SET(MOSEK_PLATFORM_NAME "win64x86") | ||
else() | ||
message(FATAL_ERROR "Mosek not supported for ${CMAKE_SYSTEM}") | ||
endif() | ||
|
||
function(FindMosekPlatformInPath RESULT PATH) | ||
SET(${RESULT} "") | ||
if(EXISTS "${PATH}/mosek") | ||
SET(dirlist "") | ||
FILE(GLOB entries LIST_DIRECTORIES true "${PATH}/mosek/*") | ||
FOREACH(f ${entries}) | ||
if(IS_DIRECTORY "${f}") | ||
get_filename_component(bn "${f}" NAME) | ||
if("${bn}" MATCHES "^[0-9]+[.][0-9]+$") | ||
if (${bn} GREATER_EQUAL "10.0") | ||
LIST(APPEND dirlist "${bn}") | ||
endif() | ||
endif() | ||
endif() | ||
ENDFOREACH() | ||
LIST(SORT dirlist COMPARE NATURAL ORDER DESCENDING) | ||
|
||
if(MOSEK_PLATFORM_NAME) | ||
foreach(MOSEK_VERSION ${dirlist}) | ||
SET(MSKPFDIR "${PATH}/mosek/${MOSEK_VERSION}/tools/platform/${MOSEK_PLATFORM_NAME}") | ||
if(EXISTS "${MSKPFDIR}") | ||
SET(${RESULT} ${MSKPFDIR} PARENT_SCOPE) | ||
return() | ||
endif() | ||
endforeach() | ||
endif() | ||
endif() | ||
endfunction() | ||
|
||
|
||
function(MosekVersionFromPath RESVMAJOR RESVMINOR PATH) | ||
execute_process(COMMAND "${PATH}/bin/mosek" "-v" RESULT_VARIABLE CMDRES OUTPUT_VARIABLE VERTEXT) | ||
if (${CMDRES} EQUAL 0) | ||
if (${VERTEXT} MATCHES "^MOSEK version [0-9]+[.][0-9]+[.][0-9]+") | ||
string(REGEX REPLACE "^MOSEK version ([0-9]+)[.]([0-9]+).*" "\\1;\\2" MSKVER ${VERTEXT}) | ||
|
||
list(GET MSKVER 0 VMAJOR) | ||
list(GET MSKVER 1 VMINOR) | ||
set(${RESVMAJOR} "${VMAJOR}" PARENT_SCOPE) | ||
set(${RESVMINOR} "${VMINOR}" PARENT_SCOPE) | ||
|
||
#message(STATUS "mskver = ${VMAJOR}.${VMINOR} -> ${${RESVMAJOR}}.${${RESVMINOR}}") | ||
#message(STATUS " ${RESVMAJOR} = ${${RESVMAJOR}}") | ||
#message(STATUS " ${RESVMINOR} = ${${RESVMINOR}}") | ||
return() | ||
endif() | ||
endif() | ||
endfunction() | ||
|
||
|
||
# Where to look for MOSEK: | ||
# Standard procedure in Linux/OSX is to install MOSEK in the home directory, i.e. | ||
# $HOME/mosek/X.Y/... | ||
# Option 1. The user can specify when running CMake where the MOSEK platform directory is located, e.g. | ||
# -DMOSEK_PLATFORM_DIR=$HOME/mosek/10.2/tools/platform/linux64x86/ | ||
# in which case no search is performed. | ||
# Option 2. The user can specify MOSEK_ROOT when running cmake. MOSEK_ROOT is | ||
# the directory where the root mosek/ tree is located. | ||
# Option 3. Automatic search. We will then attempt to search in the default | ||
# locations, and if that fails, assume it is installed in a system location. | ||
# For option 2 and 3, the newest MOSEK version will be chosen if more are available. | ||
|
||
if(MOSEK_PLATFORM_DIR) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would use From $ git grep -n "ROOT_DIR\b" | wc -l
209
$ git grep -n "PLATFORM_DIR\b" | wc -l
0 |
||
# User defined platform dir directly | ||
elseif(MOSEK_BASE) | ||
# Look under for MOSEK_ROOT/X.Y | ||
FindMosekPlatformInPath(MOSEK_PLATFORM_DIR "${MOSEK_BASE}") | ||
if(NOT MOSEK_PLATFORM_DIR) | ||
message(FATAL_ERROR " Could not locate MOSEK platform directory under ${MOSEK_BASE}") | ||
endif() | ||
endif() | ||
if(NOT MOSEK_PLATFORM_DIR) | ||
# Look in users home dir | ||
if(EXISTS $ENV{HOME}) | ||
FindMosekPlatformInPath(MOSEK_PLATFORM_DIR "$ENV{HOME}") | ||
endif() | ||
endif() | ||
if(NOT MOSEK_PLATFORM_DIR) | ||
# Look in users home dir | ||
if(EXISTS "$ENV{HOMEDRIVE}$ENV{HOMEPATH}") | ||
FindMosekPlatformInPath(MOSEK_PLATFORM_DIR "$ENV{HOMEDRIVE}$ENV{HOMEPATH}") | ||
endif() | ||
endif() | ||
|
||
if(NOT MOSEK_PLATFORM_DIR) | ||
message(FATAL_ERROR " MOSEK_PLATFORM_DIR could not be detected") | ||
else() | ||
MosekVersionFromPath(MSKVMAJOR MSKVMINOR "${MOSEK_PLATFORM_DIR}") | ||
if(MSKVMAJOR) | ||
message(STATUS " MOSEK ${MSKVMAJOR}.${MSKVMINOR}: ${MOSEK_PLATFORM_DIR}") | ||
set(MOSEK_PFDIR_FOUND TRUE) | ||
else() | ||
message(STATUS " Found MOSEK_PLATFORM_DIR, but failed to determine version") | ||
endif() | ||
endif() | ||
|
||
if(MOSEK_PFDIR_FOUND AND MSKVMAJOR AND MSKVMINOR AND NOT TARGET mosek::mosek) | ||
add_library(mosek::mosek UNKNOWN IMPORTED) | ||
|
||
find_path(MOSEKINC mosek.h HINTS ${MOSEK_PLATFORM_DIR}/h) | ||
if(WIN32) | ||
find_library(LIBMOSEK mosek64_ HINTS ${MOSEK_PLATFORM_DIR}/bin) | ||
else() | ||
find_library(LIBMOSEK mosek64 HINTS ${MOSEK_PLATFORM_DIR}/bin) | ||
endif() | ||
|
||
if(LIBMOSEK) | ||
set_target_properties(mosek::mosek PROPERTIES IMPORTED_LOCATION ${LIBMOSEK}) | ||
endif() | ||
|
||
if (MOSEKINC) | ||
target_include_directories(mosek::mosek INTERFACE "${MOSEKINC}") | ||
endif() | ||
elseif(NOT TARGET mosek::mosek) | ||
add_library(mosek::mosek UNKNOWN IMPORTED) | ||
|
||
find_path(MOSEKINC mosek.h) | ||
find_library(LIBMOSEK mosek64) | ||
|
||
if(LIBMOSEK) | ||
set_target_properties(mosek::mosek PROPERTIES IMPORTED_LOCATION ${LIBMOSEK}) | ||
endif() | ||
|
||
if (MOSEKINC) | ||
target_include_directories(mosek::mosek INTERFACE "${MOSEKINC}") | ||
endif() | ||
endif() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
#include <fstream> | ||
#include <iostream> | ||
#include <ostream> | ||
#include <limits> | ||
#include <sstream> | ||
|
||
#include "absl/log/check.h" | ||
#include "absl/memory/memory.h" | ||
#include "absl/status/statusor.h" | ||
#include "ortools/base/init_google.h" | ||
#include "ortools/math_opt/cpp/math_opt.h" | ||
|
||
const double inf = std::numeric_limits<double>::infinity(); | ||
namespace math_opt = ::operations_research::math_opt; | ||
using math_opt::LinearExpression; | ||
|
||
|
||
/// Build and solve a basic Markowit portfolio problem. | ||
/// | ||
/// # Arguments | ||
/// - mu The vector of length n of expected returns on the assets | ||
/// - GT A vector defining a m x n matrix in row-major format. It is the | ||
/// facored co-variance matrix, i.e. the covariance matrix is Q = G*G^T | ||
/// - x0 A vector of length n of initial investments | ||
/// - w Initial wealth not invested yet. | ||
/// - gamma The risk bound as a bound on the variance of the return of portfolio, | ||
/// gamma > sqrt( xGG^Tx ). | ||
absl::StatusOr<std::tuple<double,std::vector<double>>> basic_markowitz( | ||
const std::vector<double> & mu, | ||
const std::vector<double> & GT, | ||
const std::vector<double> & x0, | ||
double w, | ||
double gamma, | ||
math_opt::SolverType st) { | ||
|
||
math_opt::Model model("portfolio_1_basic"); | ||
size_t n = mu.size(); | ||
size_t m = GT.size()/n; | ||
std::vector<math_opt::Variable> x; x.reserve(n); | ||
for (int i = 0; i < n; ++i) | ||
x.push_back(model.AddContinuousVariable(0.0,inf,(std::ostringstream() << "x" << i).str())); | ||
|
||
model.Maximize(LinearExpression::InnerProduct(x,mu)); | ||
|
||
double totalwealth = w; for (double v : x0) w += v; | ||
model.AddLinearConstraint(LinearExpression::Sum(x) == totalwealth, "Budget"); | ||
|
||
std::vector<math_opt::LinearExpression> linear_to_norm; | ||
for (int i = 0; i < m; ++i) { | ||
linear_to_norm.push_back(LinearExpression::InnerProduct(absl::Span(GT.data()+m*i,m), x)); | ||
} | ||
model.AddSecondOrderConeConstraint(linear_to_norm, gamma, "risk"); | ||
|
||
// Set parameters, e.g. turn on logging. | ||
math_opt::SolveArguments args; | ||
args.parameters.enable_output = true; | ||
|
||
// Solve and ensure an optimal solution was found with no errors. | ||
const absl::StatusOr<math_opt::SolveResult> result = | ||
math_opt::Solve(model, st, args); | ||
if (! result.ok()) | ||
return result.status(); | ||
|
||
double pobj = result->objective_value(); | ||
std::vector<double> res(n); | ||
for (int i = 0; i < n; ++i) | ||
res[i] = result->variable_values().at(x[i]); | ||
|
||
return std::make_tuple(pobj,std::move(res)); | ||
} | ||
|
||
int main(int argc, char ** argv) { | ||
const int n = 8; | ||
const double w = 59.0; | ||
std::vector<double> mu = {0.07197349, 0.15518171, 0.17535435, 0.0898094 , 0.42895777, 0.39291844, 0.32170722, 0.18378628}; | ||
std::vector<double> x0 = {8.0, 5.0, 3.0, 5.0, 2.0, 9.0, 3.0, 6.0}; | ||
double gamma = 36; | ||
std::vector<double> GT = { | ||
0.30758, 0.12146, 0.11341, 0.11327, 0.17625, 0.11973, 0.10435, 0.10638, | ||
0. , 0.25042, 0.09946, 0.09164, 0.06692, 0.08706, 0.09173, 0.08506, | ||
0. , 0. , 0.19914, 0.05867, 0.06453, 0.07367, 0.06468, 0.01914, | ||
0. , 0. , 0. , 0.20876, 0.04933, 0.03651, 0.09381, 0.07742, | ||
0. , 0. , 0. , 0. , 0.36096, 0.12574, 0.10157, 0.0571 , | ||
0. , 0. , 0. , 0. , 0. , 0.21552, 0.05663, 0.06187, | ||
0. , 0. , 0. , 0. , 0. , 0. , 0.22514, 0.03327, | ||
0. , 0. , 0. , 0. , 0. , 0. , 0. , 0.2202 }; | ||
|
||
|
||
auto res = basic_markowitz(mu,GT,x0,w,gamma,math_opt::SolverType::kMosek); | ||
|
||
if (! res.ok()) { | ||
std::cerr << "Failed to solve problem: " << res.status() << std::endl; | ||
} | ||
else { | ||
auto &[pobj,xx] = *res; | ||
std::cout << "Primal Objective value: " << pobj << std::endl; | ||
std::cout << "Solution values:" << std::endl; | ||
for (int i = 0; i < xx.size(); ++i) | ||
std::cout << " x["<< i << "] = " << xx[i] << std::endl; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/USE_HIGHS/USE_MOSEK/
;)